HDU-1079 Calendar Game(博弈论)

来源:互联网 发布:手机藏文输入法软件 编辑:程序博客网 时间:2024/06/05 20:04

题目链接

HDU-1079 Calendar Game

题目大意

给定一个2001/11/04前的合法日期,每次可以变成下一天,或者变成下一个月的同一天(下个月必须有这一天),两个人轮流变化,问先手是否能必定先到2001/11/04

Sample Input

3
2001 11 3
2001 11 2
2001 10 3

Sample Output

YES
NO
NO

思路

网上很多通过奇偶判断的都没给出具体解释,在题目的Discuss中找到一个很好的解释:解题说明
又看到可以逆推出当前日期的答案,感觉更容易理解,就先写了。

必胜点和必败点的性质:
1、所有终结点是 必败点P。
2、从任何必胜点N 操作,至少有一种方式可以进入 必败点P。
3、无论如何操作,必败点P 都只能进入 必胜点N。

逆推时抓住上面的性质即可推出正确答案。

代码

#include <cstdio>#include <cstring>#include <algorithm>using namespace std;int n;int dp[2007][17][37],yy,mm,dd;const int days[2][13]={{0,31,28,31,30,31,30,31,31,30,31,30,31},                       {0,31,29,31,30,31,30,31,31,30,31,30,31}};inline int leapYear(int y) {    return (y%4==0&&(y%100!=0||y%400==0))?1:0;}bool solve(int y,int m,int d) {    if(dp[y][m][d]!=-1) {        return dp[y][m][d]==1;    }    if(d>days[leapYear(y)][m]) {//日期不合法时,认为当前日期为必胜点        dp[y][m][d]=1;        return true;    }    yy=y;    mm=m+1;    dd=d;    if(mm>12) {        ++yy;        mm=1;    }    dp[y][m][d]=(solve(yy,mm,dd)?0:1);//下一个月同一天    if(dp[y][m][d]==0) {        yy=y;        mm=m;        dd=d+1;        if(dd>days[leapYear(yy)][mm]) {            dd=1;            if(++mm>12) {                mm=1;                ++yy;            }        }        dp[y][m][d]=(solve(yy,mm,dd)?0:1);//明天    }    return dp[y][m][d]==1;}int main() {    int T,y,m,d;    scanf("%d",&T);    memset(dp,-1,sizeof(dp));    for(int i=0;i<=31;++i) {//超过比赛日期则认为是该日期必胜点        dp[2001][11][i]=dp[2001][12][i]=1;    }    dp[2001][11][1]=dp[2001][11][2]=dp[2001][11][3]=-1;    dp[2001][11][4]=0;    while(T-->0) {        scanf("%d%d%d",&y,&m,&d);        printf("%s\n",solve(y,m,d)?"YES":"NO");    }    return 0;}
0 0