hdu 1079 Calendar Game(博弈SG函数 || 奇偶找规律)

题意:

从当前日期,在他/她转的玩家可以移动到下一个历日或下月的同一天。当在之后的一个月中没有在同一天,播放器只能移动到下一个的日历日期。例如,从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)的是必胜点,由时间从后向前推。

最后若输入的sg[] = 0即为必败点,输出 NO

代码:

#include<iostream>
#include<string.h>
using namespace std;
int sg[500][20][40];
int day[400];
int mm[15]={0,31,28,31,30,31,30,31,31,30,31,30,31};

int g(int y , int m , int d)
{
    if(sg[y][m][d]!=-1) return sg[y][m][d];
    int flag=0;
    if(y>101) return sg[y][m][d]=0;
    if(m==2 && d==day[y] || m<12 && mm[m]==d && mm[m+1]>=d)
    {
        int a=g(y,m+1,d) , b=g(y,m+1,1);
        if(a==0 || b==0) return sg[y][m][d]=1;
        else if(a && b)  return sg[y][m][d]=0;
    }
    else if(m<12 && mm[m]==d && mm[m+1]<d)
    { 
        int a=g(y,m+1,1); 
        if(a==0)  return sg[y][m][d]=1;
        else return sg[y][m][d]=0;
    }
    else if(m==2 && d<day[y] || m<12 && d<mm[m] || m==12 && d<mm[12])
    { //cout<<"**************"<<endl; 
        int a=g(y,m,d+1) , b=g(y , m+1 , d);// cout<<"a= "<<a<<"  b= "<<b<<endl;
        if(a==0 || b==0 ) return sg[y][m][d]=1;
        else if(a && b) return sg[y][m][d]=0;
    }
    else if(m==12 && d==mm[12])
    {
        int a=g(y+1 ,1 ,1) , b=g(y+1 ,1, d);
        if(a==0 || b==0) return sg[y][m][d]=1;
        else if(a && b) return sg[y][m][d]=0;
    }
}

int main()
{
   int y,m,d;
   int t;
   memset(sg,-1,sizeof(sg));
   for(int i=1900;i<=2001;i++)
   if(i%4==0&&i%100!=0 || i%400==0) day[i-1900+1]=29; 
   else day[i-1900+1]=28;  
   sg[101][11][4]=0;
   for(int i=5;i<=mm[11];i++) sg[101][11][i]=1;
   for(int i=1;i<=mm[12];i++) sg[101][12][i]=1;
   scanf("%d",&t);
   while(t--)
   {
       scanf("%d%d%d",&y,&m,&d);
       y-=1900; 
       g(y,m,d);// cout<<"**= "<<sg[101][11][4]<<"   "<<sg[101][11][3]<<endl;
       if(sg[y][m][d]==1) printf("YES\n");
       else printf("NO\n");
   }
   return 0;
}

找规律,不管是月份加一,还是日期加一,都改变了奇偶性,只有两个特殊日期9月30日,和11月30日例外(不管该年是否为润年,2月28\ 29同样一步都会发生正常奇偶变化)。

那么目标日期是11月4日,为奇数。初始日期如果为偶数的话,先者必胜。

考虑特殊是日期,两个特殊日期本来为奇数,可以做到移动一步还是奇数,那么必胜点与必败点发生变化。

那么会不会在中途经过这两个日期呢。

如果本来为偶数,如果经过特殊日期就会改变奇偶,从必胜到必败。作为先手,不会主动进入特殊日期,而后者不可能从奇数依旧到达特殊日期的奇数。

如果本来为奇数,同样先手想赢,但是不可能进入特殊日期。保持奇偶性交替变化。

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

 代码:

#include<iostream>
#include<string.h>
using namespace std;
int main()
{
    int y,m,d , t;
    scanf("%d",&t);
    while(t--)
    {
         scanf("%d%d%d",&y,&m,&d);
         if((m+d)%2==0 || m==9 && d==30 || m==11 && d==30)  printf("YES\n");
         else printf("NO\n");
    }
    return 0;
}



  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值