这是一道典型的博弈论的题目,对于博弈论题目我的想法是从后往前倒推,用寻找必败状态的方法解决。
但是我没有找到很好的判定必败的规律,于是我决定列出递归判断出1900.1.1到2001.11.4日之间所有的情况,把每一天是必败还是必胜的情况先计算出来做好标记,之后的输入可以直接在这张表中查询。
一开始的代码:
#includeint month_day[13]={ 0,31,28,31,30,31,30,31,31,30,31,30,31};int isWin[102][13][32]; //1900年作为第0年int judge(int year,int month,int day); //判断输赢 1亚当赢 0亚当输 -1当前还未判定int main(void){ for(int i=0;i<102;i++) for(int j=0;j<13;j++) for(int k=0;k<32;k++) isWin[i][j][k]=-1; int n,year,month,day; scanf("%d",&n); while(n--) { scanf("%d %d %d",&year,&month,&day); if(judge(year-1900,month,day)==1) printf("Yes\n"); else if((judge(year-1900,month,day)==0)) printf("No\n"); } return 0;}//判断输赢 用递归的方法进行模拟搜索int judge(int year,int month,int day){ int win; //递归结束的条件 if(year>101||year==101&&month>11||year==101&&month==11&&day>4) return 1; if(year==101&&month==11&&day==4) return 0; if(isWin[year][month][day]==-1) { win=0; //首先改变月份,如果下一月的同一天状态为必败当前肯定为赢 if(month!=12) { if(day<=month_day[month+1]||(day==29&&((year%4==0&&year%100!=0)||year%400==0))) if(judge(year,month+1,day)==0) win=1; } //如果当前是12月份 else if(judge(year+1,1,day)==0) win=1; //如果改变月份没有获胜的话,接着改变天数 if(win==0) { if(day
之后在网上看到了一个很简单的方法:
第一个必败状态是2001.11.04。由此可以推出其他任何时间的状态。对于除2001.11.04外的其他任何时间,present状态是由能移动到的下两个next状态决定的(当然有些时间只有一个next状态),比如1924.12.19的状态是由1924.12.20和1925.01.19两个状态决定。如果两个next状态中有一个必败状态,则present状态为必胜状态;如果两个next状态都为必胜状态,则present状态为必败状态。
对于2001年11月的那4天,状态都是交替胜负的。1和3号必胜,2和4号必败。现在考虑10月份,5-31号只有一个next状态,推算可知奇数号状态为必败,偶数号状态为必胜。1-4号状态有两个next状态,推算可知也是奇数号状态为必败,偶数号状态为必胜。也就是说整个10月份奇数号状态为必败,偶数号状态为必胜。
由此我们可以推测如果每个月都是31天的话,那么每天的状态都是相反的,而且相邻的两个月的同一天状态也是相反的。即奇数月的奇数号状态为必胜,偶数号为必败;偶数月偶数号状态为必胜,奇数号状态为必败。从数学上说,就是月与号和为偶数的天状态为必胜,为奇数的天状态为必败。显然这个是成立的。
接下来要考虑特殊情况,那几个只有30天的月份。有30号的有4,6,9,11这四个月。对于04.30,next状态有05.01和05.30,显然两个next状态是相反的,所以04.30的状态是必胜的。所以04.30的状态情况符合上面那个结论。06.30同样如此。对于09.30,next状态有10.01和10.30,同样10.01和10.30的状态是相反的,所以09.30的状态为必胜,这不符合上面的结论。但是我们可以证明这只是一种特殊情况,不影响整个结论。按照原来的结论,九月份的奇数号状态为必胜,偶数号状态为必败。现在30号的状态变化了,如果我们能证明29号的状态不会因此发生变化,那么特殊情况就只局限于30号了。09.29号的next状态有09.30和10.29,10.29的状态为必败,所以09.29的状态为必胜,还是符合原来的结论。11.30同样如此。
最后考虑特殊的2月份。如果是闰年的29天,效果和31天一个月是一样的(只要是奇数都一样,哪怕一个月只有一天)。对于非闰年,2月只有28天。其实28天也等同于30天的情况,推算可知02.28和04.30,06.30一样,不影响整个结论。
总结,月与号和为偶数的天状态为必胜,为奇数的天状态为必败。特殊情况为09.30和11.30,这两天的状态也为必胜。
照着这个思路,写了以下的代码:
#includeint main(){ int year, month, day; int n; scanf("%d",&n); while (n--) { scanf("%d %d %d", &year, &month, &day); if ((month== 9 || month== 10) && day == 30) { printf("YES\n"); } else if ((month+ day) % 2 == 1) { printf("NO\n"); } else { printf("YES\n"); } } return 0;}