求sg值以及找规律

题目:hdu1079

题意:随机给一个日期,两个人轮流计数,可以将月份加一,也可以将日期加一,但是要符合日期的客观规律(即要考虑平年闰年和日期进位下的进位情况),如果是2001年1月31日则日期+1后变成 2001年2月1日,因为2月没有31天故不可以将月份加一。先到达2001.11.4的获胜。

解答:方法一:求sg值。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int MAXN = 100;
int sg[2002][15][35];
int Map[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
bool judgey(int a)//看平年还是闰年
{
    if((a % 4 == 0 && a % 100 != 0) || (a % 100 == 0 && a % 400 == 0))
    {
        return true;
    }
    else
        return false;
}
bool judge(int a,int b,int c)//检查日期是否是在结束日期之前
{
    if(a > 2001)
        return true;
    else if(a == 2001 && b == 10 && c > 4)
    {
        return true;
    }
    else if(a == 2001 && b > 10)
    {
        return true;
    }
    else
        return false;
}
int getsg(int a,int b,int c)
{
    if(sg[a][b][c] != -1)
        return sg[a][b][c];
    int vis[MAXN];
    memset(vis,0,sizeof(vis));
    if(a == 2001 && b == 10 && c == 4)
    {
        sg[a][b][c] = 0;
        return 0;
    }
    if(judgey(a))
        Map[1] = 29;
    else
        Map[1] = 28;
    if(c <= Map[(b+1)%12])
    {
        if(b == 11)//加一个月到第二年了
        {
            if(!judge(a+1,0,c)){
            vis[getsg(a+1,0,c)] = 1;}
        }
        else{//加一个月到今年的下一个月
            if(!judge(a,b+1,c)){
            vis[getsg(a,b+1,c)] = 1;}
        }
    }
        if(c < Map[b]){//当前月加一天
            if(!judge(a,b,c+1)){
            vis[getsg(a,b,c+1)] = 1;}
        }
        else//加一天到下一个月了
        {
            if(b == 11){
            if(!judge(a+1,0,1)){
                vis[getsg(a+1,0,1)] = 1;}
            }
            else{
            if(!judge(a,b+1,1)){
                vis[getsg(a,b+1,1)] = 1;}
            }
        }
    for(int i = 0;;i++)
    {
        if(!vis[i])
        {
            sg[a][b][c] = i;
            return i;
        }
    }
}
int main()
{
    int T,y,m,d;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&y,&m,&d);
        memset(sg,-1,sizeof(sg));
        int ans = getsg(y,m-1,d);
        if(ans == 0)
            puts("NO");
        else
            puts("YES");
    }
    return 0;
}

方法二:找规律。(啊这个看了网上的题解)

假如现在离终点日期还是比较远的,就是不考虑临近终点时候的情况。 考虑M + D的奇偶性质。加一个日期在不产生进位的情况下奇偶是变化的。在进位的情况下,奇偶变化的天数是2.28、4.30、6.30、9.30、11.30(即这些日子如果加一天不会改变奇偶性) 如果我现在处于优势的状态,那么我肯定不会主动选择在这些点进行日期+1操作。现在再考虑能不能想办法避免让对手也进行+1操作。每一个日期的下一步都有两个选择。则完全可以避免不给对手留下这些点。

所以我们已经证明了,在距离结果比较远的地方主动方完全一直把握主动权。在距离终点比较远的时候只有9 11两个月是奇数月(可以加一天还是奇数)。11.4的日期和是奇数,所以我们猜测当拿到月份和日期的和是偶数的时候还是比较容易取得胜利的。

所以计算开始给出的日期的月份和日期和,再特判 9 和 11的三十号即可AC。

#include <iostream>
#include <iomanip>
#include <queue>
#include <stack>
#include <cstdio>
#include <cstring>
#include <map>
using namespace std;

int main()
{
	int casen, y, m, d;
	scanf("%d", &casen);
	while(casen--){
		scanf("%d%d%d", &y, &m, &d);
		if((m == 11 || m == 9) && d == 30){
			cout << "YES" << endl;
			continue;
		}
		if((m + d) % 2 == 0) cout << "YES" << endl;
		else cout << "NO" << endl;
	}
	return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值