「NOIP模拟赛」日历游戏
「问题描述」
moreD和moreD的宠物CD正在玩一个日历游戏,开始时,他们从1900年1月1日到2012年12月22日(你懂的……)选一个日期开始,依次按照如下规则之一向后跳日期:
- 1. 跳到日历上的下一天。
- 2. 跳到日历上的下个月的同一天(如果不存在,则不能这么做)。
要是谁正好到达2012年12月22日那么他就赢了,如果到达这天之后的日期那他就输了——原因你也懂的。
每次都是moreD先走的。
现在,给你一个日期,请问moreD一定能赢吗?
「输入」
输入共T行,每行三个整数,Y、M、D,分别表示年、月、日。日期在1900年1月1日到2012年12月22日之间(包含两端)。
T并不在输入数据当中。
「输出」
要是moreD一定能赢,输出一行YES,否则输出NO。
「输入输出样例一」
calendar.in |
calendar.out |
2012 12 20 |
NO |
「输入输出样例二」
calendar.in |
calendar.out |
2012 12 21 |
YES |
「数据描述」
对于50%的数据,是1949年1月1日后的日期。 T <= 5
对于100%的数据,是1900年1月1日后的日期。T <= 10
题解
对于日期直接的关系是一棵二叉树,然后用记忆化搜索解决
具体可以递推或者用sg函数
为了防止爆栈可以先分成几棵子树处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstdlib> #include<cstring> using namespace std; int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int T; int a[12]={31,28,31,30,31,30,31,31,30,31,30,31}; int f[3005][15][40]; bool lea(int x) { if(x%400==0||(x%4==0&&x%100!=0))return 1; return 0; } bool jud(int x,int y,int z) { if(x>2012||y>12||z>31)return 0; if(x==2012&&y==12&&z>=23)return 0; if(y==2) { if(z>a[1]+lea(x))return 0; } else if(z>a[y-1])return 0; return 1; } int dp(int x,int y,int z) { if(x==2012&&y==12&&z==22)return 0; if(f[x][y][z]!=-1)return f[x][y][z]; int sg[3];memset(sg,0,sizeof(sg)); int tx=x,ty=y,tz=z,lim=a[y-1]; tz++; if(y==2&&lea(tx))lim++; if(tz==lim+1)tz=1,ty++; if(ty==13)tx++,ty=1; if(jud(tx,ty,tz))sg[dp(tx,ty,tz)]=1; tx=x,ty=y,tz=z; ty++; if(ty==13)tx++,ty=1; if(jud(tx,ty,tz))sg[dp(tx,ty,tz)]=1; for(int i=0;i<=3;i++) if(!sg[i]) return f[x][y][z]=i; } int main() { memset(f,-1,sizeof(f)); int x,y,z; while(scanf("%d%d%d",&x,&y,&z)!=EOF) { for(int i=2000;i>=1900;i--)dp(i,1,1); if(dp(x,y,z))puts("YES"); else puts("NO"); } return 0; } |
Subscribe