题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1079
题面:
3 2001 11 3 2001 11 2 2001 10 3
YES NO NO
给定一个初始日期,对于当前日期,A,B轮流操作。操作方式有两种,以天数为单位后移一天,或者以月为单位,后移一月,但如果后面那个月份没有对应的天,那么就不能进行月移操作。若刚好移到指定日期者获胜。
解题:
抛开日期操作不说,这是一道很简单的博弈问题,但由于结合了日期操作,所以处理较繁琐。博弈策略是这样的,对于一个日期,如果能够通过日移操作或月移操作达到一个必败态,那么该状态就为胜态,否则就为必败态。也就是去检验后一个月,和后一天的状态。这道题的亮点在于,出了任何小差错,就可能导致不能AC。
提升版:
ZJUT 1587
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <vector> #include <cmath> #define eps 1e-7 using namespace std; struct date { int y,m,d; }; //日期状态 bool status[2016][13][32],flag,sign; //每月天数 int month[13]={0,31,28,31,30,31,30,31,31,30,31,30,31}; //日移操作 date next_day(int y,int m,int d) { date tmp; //如果是12月份,可能会换年,需特殊处理 if(m==12) { if(d==31) tmp.y=y+1,tmp.m=1,tmp.d=1; else tmp.y=y,tmp.m=12,tmp.d=d+1; } //如果不是2月,一般处理 else if(m!=2) { //看是不是该月最后一天,可能会换月,需特殊处理 if(d==month[m]) tmp.y=y,tmp.m=m+1,tmp.d=1; else tmp.y=y,tmp.m=m,tmp.d=d+1; } //如果是2月,需要看是不是闰年 else { if(y%400==0||(y%4==0&&y%100!=0)) { if(d==29) tmp.y=y,tmp.m=3,tmp.d=1; else tmp.y=y,tmp.m=2,tmp.d=d+1; } else { if(d==28) tmp.y=y,tmp.m=3,tmp.d=1; else tmp.y=y,tmp.m=2,tmp.d=d+1; } } return tmp; } //月移操作 date next_month(int y,int m,int d) { date tmp; //flag为月移是否合法标志 flag=true; //特判12月,可能会跨年 if(m==12) tmp.y=y+1,tmp.m=1,tmp.d=d; //特判1、2月 else if((m!=2)&&(m!=1)) { //到达月末,换月 if(d>month[m+1]) flag=false; else tmp.y=y,tmp.m=m+1,tmp.d=d; } //1月,因为后移到2月,要看2月是否在闰年 else if(m==1) { if((y%4==0&&y%100!=0)||(y%400==0)) { if(d>29) flag=false; else tmp.y=y,tmp.m=2,tmp.d=d; } else { if(d>28) flag=false; else tmp.y=y,tmp.m=2,tmp.d=d; } } //2月最多29天,对于3月都是可以的 else tmp.y=y,tmp.m=m+1,tmp.d=d; return tmp; } //年移操作,此题不需要 date next_year(int y,int m,int d) { //sign为年移是否合法标志 sign=true; date tmp; //只有2月29号是特殊的 if(m==2&&d==29) sign=false; else tmp.y=y+1,tmp.m=m,tmp.d=d; return tmp; } int main() { date tmp,tmp2; //标记11.1-11.4号 for(int i=1;i<=4;i++) { if(i%2) status[2001][11][i]=0; else status[2001][11][i]=1; } if(status[2001][11][1]) status[2001][10][31]=0; else status[2001][10][31]=1; //10.5-10.30只能日移,不能月移 for(int i=30;i>=5;i--) { tmp=next_day(2001,10,i); //如果后一天是必败态,那么当前就是胜态 if(status[tmp.y][tmp.m][tmp.d]) status[2001][10][i]=0; else status[2001][10][i]=1; } //10.1-10.4既可月移也可日移 for(int i=4;i>=1;i--) { tmp=next_day(2001,10,i); tmp2=next_month(2001,10,i); //如果月移或日移到必败态,那么当前为胜态,否则为必败态 if(status[tmp.y][tmp.m][tmp.d]||status[tmp2.y][tmp2.m][tmp2.d]) status[2001][10][i]=0; else status[2001][10][i]=1; } //2001.1-2001.9既可月移又可日移 for(int i=9;i>=1;i--) { for(int j=month[i];j>=1;j--) { tmp=next_day(2001,i,j); if(status[tmp.y][tmp.m][tmp.d]) status[2001][i][j]=0; else { tmp=next_month(2001,i,j); if(flag) { if(status[tmp.y][tmp.m][tmp.d]) status[2001][i][j]=0; else status[2001][i][j]=1; } else status[2001][i][j]=1; } } } //1900-2000,即可月移又可日移 for(int i=2000;i>=1900;i--) { for(int j=12;j>=1;j--) { int k; if(j==2&&(i%4==0&&i%100!=0)||(i%400==0)) k=29; else k=month[j]; for(;k>=1;k--) { tmp=next_day(i,j,k); if(status[tmp.y][tmp.m][tmp.d]) status[i][j][k]=0; else { tmp=next_month(i,j,k); if(flag) { if(status[tmp.y][tmp.m][tmp.d]) status[i][j][k]=0; else status[i][j][k]=1; } else status[i][j][k]=1; } } } } //输入输出 int yy,mm,dd,T; cin>>T; while(T--) { cin>>yy>>mm>>dd; if(status[yy][mm][dd]) cout<<"NO\n"; else cout<<"YES\n"; } return 0; }