【HDU1079】Calendar Game(博弈,PN状态枚举)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1079

大意是两个人从1900年1月1日之后的某一天开始走,每次可以走到下一天或者下个月的这一天(如果存在的话),谁先走到2001年11月4日就获胜。学习完博弈后最先想到的办法是求PN状态,数据量比较大而且没有找到规律,于是就从后往前枚举了一下每天的PN态,存储在sg数组中,就过了。

这是代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <stack>
#include <queue>
#include <iostream>
#include <algorithm>
using namespace std;

int sg[200][15][50];
int feb[200];
int days[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };

int getsg(int y,int m,int d)
{
    if(y>1000) y -= 1900;

    if(sg[y][m][d] != -1){
        return sg[y][m][d];
    }
    if(m==12){
        if(d==31){
            int a = getsg(y+1,1,1), b = getsg(y+1,1,d);
            if(a && b) sg[y][m][d] = 0;
            else sg[y][m][d] = 1;
        }
        else{
            int a = getsg(y,m,d+1), b = getsg(y+1,1,d);
            if(a && b) sg[y][m][d] = 0;
            else sg[y][m][d] = 1;
        }
    }
    else if(m==1){
        if(d <= feb[y]){
            int a = getsg(y,m,d+1), b = getsg(y,m+1,d);
            if(a && b) sg[y][m][d] = 0;
            else sg[y][m][d] = 1;
        }
        else{
            int a;
            if(d==31) a = getsg(y,m+1,1);
            else a = getsg(y,m,d+1);
            if(a) sg[y][m][d] = 0;
            else sg[y][m][d] = 1;
        }
    }
    else if(d <= days[m+1]){
        if(d==days[m]){
            int a = getsg(y,m+1,1), b = getsg(y,m+1,d);
            if(a && b) sg[y][m][d] = 0;
            else sg[y][m][d] = 1;
        }
        else{
            int a = getsg(y,m,d+1), b = getsg(y,m+1,d);
            if(a && b) sg[y][m][d] = 0;
            else sg[y][m][d] = 1;
        }
    }
    else if(d > days[m+1]){
        if(d==days[m]){
            int a = getsg(y,m+1,1);
            if(a) sg[y][m][d] = 0;
            else sg[y][m][d] = 1;
        }
        else{
            int a = getsg(y,m,d+1);
            if(a) sg[y][m][d] = 0;
            else sg[y][m][d] = 1;
        }
    }
    else printf("******************include error at : %d %d %d\n",y+1900,m,d);
    return sg[y][m][d];
}

bool isleap(int year)
{
    if( (year%4==0 && year%100!=0) || year%400==0 ) return true;
    return false;
}

int main()
{
    int y, m, d;
    for(int i=0;i<=101;i++){
        if(isleap(1900+i)) feb[i] = 29;
        else feb[i] = 28;
    }

    memset(sg,-1,sizeof(sg));

    sg[101][11][4] = 0;
    for(int i=5;i<=30;i++) sg[101][11][i] = 1;
    for(int i=1;i<=31;i++) sg[101][12][i] = 1;

    int cse;
    cin>>cse;
    while(cse--){

        scanf("%d %d %d",&y,&m,&d);
        int ans = getsg(y,m,d);
        if(ans) cout<<"YES"<<endl;
        else cout<<"NO"<<endl;
    }
    return 0;
}

之后看网上有人用了找规律的方法,发现正常操作都会改变奇偶性,只有9月30日和11月30日例外,但是没有看懂。。。如果是这样那代码就很短了~

#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;
}
点赞