hdu 1079 Calendar Game (SG函数)(博弈——找规律)

来源:互联网 发布:手机淘宝怎么注销账号 编辑:程序博客网 时间:2024/05/17 07:35

Calendar Game

题意:
从当前日期,在他/她转的玩家可以移动到下一个历日或下月的同一天。当在之后的一个月中没有在同一天,播放器只能移动到下一个的日历日期。例如,从1924年12月19日,你可以移动到1924年12月20日,下一个日期,或一月19日,1925年,在同一天在下个月。然而,2001年1月31日,你可以只移动2001年2月1日,因为2001年2月31日是无效的。一个球员赢得比赛时,他/她到底到达的日期2001年11月4日。如果一个玩家移动到日期2001年11月4号之后,他/她输了比赛。

思路:
(2001,11,4)是一个必败点,能到(2001,11,4)的则是必胜点,由时间从后向前推即可

代码:

#include<stdio.h>#include<string.h>#include<algorithm>using namespace std;int tday[200];//存储1900~2001每一年2月的天数int num[13]= {0,31,28,31,30,31,30,31,31,30,31,30,31};//每个月的天数int sg[200][20][40];bool is_leap(int year)//判断闰年{    if(year%400==0||(year%4==0&&year%100!=0))        return true;    return false;}int SG_dfs(int x,int y,int z){    int a=-1,b=-1;    if(sg[x][y][z]!=-1)        return sg[x][y][z];    if(x>101||(x==101&&y>11)||(x==101&&y==11&&z>4))        return sg[x][y][z]=1;    if(y==1)//跳到下一月,特判2月    {        if(z<=tday[x])            b=SG_dfs(x,2,z);    }    else//跳到下一月    {        int x1=x,y1=y+1,z1=z;        if(y1>12)            y1-=12,x1+=1;        if(z1<=num[y1])            b=SG_dfs(x1,y1,z1);    }    if(y==2)//跳到下一天,特判2月    {        if(z<tday[x])            a=SG_dfs(x,2,z+1);        else            a=SG_dfs(x,3,1);    }    else//跳到下一天    {        if(z<num[y])            a=SG_dfs(x,y,z+1);        else        {            int x1=x,y1=y+1,z1=1;            if(y1==13)                x1+=1,y1=1;            a=SG_dfs(x1,y1,z1);        }    }    if(a==0||b==0)        return sg[x][y][z]=1;    else if(a==1&&(b==-1||b==1))        return sg[x][y][z]=0;}void init(){    memset(sg,-1,sizeof(sg));    for(int i=1900; i<=2002; ++i)//预处理出每一年2月的天数        if(is_leap(i))            tday[i-1900]=29;        else            tday[i-1900]=28;    sg[101][11][4]=0;    SG_dfs(0,1,1);}int main(){    init();    int t,year,month,day;    scanf("%d",&t);    while(t--)    {        scanf("%d%d%d",&year,&month,&day);        if(!sg[year-1900][month][day])            printf("NO\n");        else            printf("YES\n");    }    return 0;}



下面是大佬找规律的证明:


找规律,发现不论是月份m+1,还是天数d+1,都改变了(m+d)的奇偶性(9月30日和11月30日例外,无论怎样操作,它们的下一步都还是奇数)

目标日期11月4日,(11+4)为奇数,那么如果开始日期为偶数的话,先手必胜。

那么会不会在中途经过这两个特殊日期呢?
如果本来为偶数,如果经过特殊日期就会改变奇偶,从必胜到必败。作为先手,不会主动进入特殊日期,而后者不可能从奇数依旧到达特殊日期的奇数。
如果本来为奇数,同样先手想赢,但是不可能进入特殊日期。保持奇偶性交替变化。

这样一来只可能是初始为特殊日期,否则中途不可能出现特殊日期

代码:

#include<stdio.h>#include<string.h>#include<algorithm>using namespace std;int main(){    int t,year,month,day;    scanf("%d",&t);    while(t--)    {        scanf("%d%d%d",&year,&month,&day);        if((month+day)%2==0||((month==9||month==11)&&day==30))            printf("YES\n");        else            printf("NO\n");    }    return 0;}
原创粉丝点击