题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=1079
非常经典的 NP状态博弈,有三种方法,记忆化搜索,逆序递推,找规律。
NP状态定理:P是“胜利终止状态”或者它的一切后继状态都是N状态,N是“失败终止状态”或者至少拥有一个后继状态是P状态。
也就是说后继状态为P的一定为N状态,不存在后继状态为N的为P状态。
- 进行记忆化搜索
将年份-1900,将其用三维数组(年月日)进行存储N/P状态,N为1,P为0。
先将2001,11,4标记为P。然后进行搜索,不断将搜索中探明的N P在数组中标记,简化下次搜索的时间,直到全部状态标记出来。
#include<stdio.h>
#include<string.h>
int sg[110][13][32];
int m[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
int judge(int year,int month,int day)
{
if(sg[year][month][day]!=-1)
return sg[year][month][day];
int year1=year+1900;
if((year1>2001)||(year1==2001&&month>11)||(year1==2001&&month==11&&day>4))
return 1;
if(year1%400==0||(year1%100!=0&&year1%4==0))//闰年
m[2]=29;
else
m[2]=28;
int ans=1;
if(month==12)///按月走
{
ans=judge(year+1,1,day);
}
else
{
if(day<=m[month+1])
ans=judge(year,month+1,day);
}
if(ans)///其后继状态是否为 P
{
if(day<m[month])
ans=judge(year,month,day+1);
else
{
if(month==12)
{
ans=judge(year+1,1,1);
}
else
ans=judge(year,month+1,1);///漏了month加一
}
}
sg[year][month][day]=ans^1;///状态与后继状态相反
return sg[year][month][day];
}
int main()
{
int t;
scanf("%d",&t);
memset(sg,-1,sizeof(sg));
sg[101][11][4]=0;
while(t--)
{
int year,month,day;
scanf("%d%d%d",&year,&month,&day);
year=year-1900;
if(judge(year,month,day)==1)
printf("YES\n");
else
printf("NO\n");
}
return 0;
}
- 逆序递推
将年份-1900,将其用三维数组(年月日)进行存储N/P状态,N为1,P为0。
先将2001,11,4标记为P。然后不断取前一天,判断前一天的后继状态,从而判断前一天的状态,直到到1900.1.1。
#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <map>
#include <set>
#include <vector>
#include <map>
#include <bitset>
#include <algorithm>
#include <queue>
#include <stack>
#include <cmath>
using namespace std;
bool sg[102][13][32];
int m[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};///天数
struct data
{
int year,month,day;
}now,tomorrow;
bool is_leap_year(int year)///判断是不是闰年
{
int year1=year+1900;
if(year1%400==0)
return true;
if(year1%100==0)
return false;
if(year1%4==0)
return true;
return false;
}
void the_last_day()
{
//int year=now.year;
int month=now.month;int day=now.day;
if(month==1&&day==1)
{
now.year--;
now.month=12;
now.day=31;
return;
}
if(is_leap_year(now.year))
m[2]=29;
else
m[2]=28;
if(day==1)
{
now.month--;
now.day=m[now.month];
return;
}
now.day--;
}
void init()
{
sg[101][11][4]=0;
now.year=101;now.month=11;now.day=3;
tomorrow.year=101;tomorrow.month=11;tomorrow.day=4;
while(true)
{
if(now.year<0)
break;
int ans=1;
ans=sg[tomorrow.year][tomorrow.month][tomorrow.day];
if(is_leap_year(now.year))
m[2]=29;
else
m[2]=28;
if(ans)///xia ge yue
{
if(now.month==12)
{
ans=sg[now.year+1][1][now.day];
}
else
{
if(now.day<=m[now.month+1])
{
if(now.year==101&&now.month+1==12);
else
{
if(now.year==101&&now.month+1==11&&now.day>4);
else
ans=sg[now.year][now.month+1][now.day];
}
}
}
}
sg[now.year][now.month][now.day]=ans^1;
tomorrow=now;
the_last_day();///更新前一天
}
}
int main()
{
int n;
init();
scanf("%d",&n);
while(n--)
{
int year,month,day;
scanf("%d%d%d",&year,&month,&day);
year=year-1900;
if(sg[year][month][day])
printf("YES\n");
else
printf("NO\n");
}
return 0;
}
- 找规律
月份+日是偶数是先手必胜,另外在9月和11月30号,也是先手必胜。其他情况先手必败。
#include <string.h>
#include <stdio.h>
#include <algorithm>
#include <iostream>
using namespace std;
int main()
{
int y,m,d;
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&y,&m,&d);
if( (d+m)%2 == 0 || (m == 9 && d == 30) || (m == 11 && d == 30) )
printf("YES\n");
else printf("NO\n");
}
return 0;
}