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

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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值