您的位置:首页 > 其它

HDU-1079 Calendar Game(博弈论)

2016-07-20 13:15 323 查看
题目链接

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  HDU 博弈论