HDU 4672 Present Day, Present Time 博弈论

73 篇文章 21 订阅
56 篇文章 0 订阅

http://acm.hdu.edu.cn/showproblem.php?pid=4672  ---> 题目地址

http://www.cnblogs.com/xin-hua/p/3259388.html----> 我参考的代码

Present Day, Present Time

题意:有 N 堆石子和 M 个石子回收站,每回合操作的人可以选择一堆石子,从中拿出一些
放到石子回收站里(可以放进多个回收站,每个回收站可以使用无数次)
,但每个石子回收站每次
只能接收特定数量的石子。不能操作的输。如果有人操作完之后,有任意一堆石子无法完全回收,
那么他直接输。
一个显然的结论是,每个游戏的 SG 值就是用 M 个回收站,完全回收这堆石子可行的最大
操作次数。由于最大的 Bi 比较小,立方暴力背包即可(比较显然的是,maxBi 2 以上的周期是
minBi )


刚看题解。你可能不太明白。。  如果你理解题目意思了的话,  只用判断两种情况


1、开始便存在一堆石子无法回收,直接输出play2赢

2、已经知道这些所有石子都能回收了,就算出每一堆的石子异或后看答案是否为0,为0则输出play2赢,否则play1赢。


因为题目数据量太大,然后得发现这样的一个情况,最大的bi<=100,而且当sg算到 max(bi) ^ 2 之后时, 它的呈周期增加的(一个周期加1),因而当数据大于bi的平方时,可以直

接用bi的平方以内的数据计算出来。

算1到bi的平方的sg时,直接暴力算就行。

一堆能被回收的石子的SG值 就是用 M 个回收站,完全回收这堆石子可行的最大操作次数


如果你不懂SG的含义。。可以看下  取石子游戏http://acm.hdu.edu.cn/showproblem.php?pid=1527 ,取石子游戏,每堆的SG值就是其一堆石子的数目

HDU 1846   http://acm.hdu.edu.cn/showproblem.php?pid=1846   

HDU 4111 http://acm.hdu.edu.cn/showproblem.php?pid=4111



然后此题就可以算出来了。


标准程序:

#include <stdio.h>
#include <string.h>
#include <vector>
#include <algorithm>

using namespace std;

typedef long long ll;
typedef pair<int,int> pii;

char player1[111];
char player2[111];
int sg[23333];
int stones[111111];
int b[111];

int main(void)
{
	int T = 0, n, m;
	while(scanf("%d%d%s%s",&n,&m,player1,player2) != EOF)
	{
		for(int i = 0;i < n;i++) scanf("%d",&stones[i]);
		for(int i = 0;i < m;i++) scanf("%d",&b[i]);
		
		int mx = *max_element(b,b+m);
		int mn = *min_element(b,b+m);
		int base = mx*mx;
		int limit = base+mn+5;
		memset(sg,-1,sizeof(int)*(limit+10));
		sg[0] = 0;
		for(int i = 0;i < m;i++)
		{
			int x = b[i];
			for(int j = 0;j <= limit;j++)
			{
				if(sg[j] < 0) continue;
				if(x+j <= limit && sg[x+j] < sg[j]+1) sg[x+j] = sg[j]+1;
			}
		}
		
		//for(int i = 0;i <= limit;i++) printf("sg[%d] = %d\n",i,sg[i]);
		
		bool fail = false;
		int result = 0;
		for(int i = 0;i < n;i++)
		{
			if(stones[i] <= limit)
			{
				if(sg[stones[i]] < 0) fail = true;
				else result ^= sg[stones[i]];
			}
			else
			{
				int down = (stones[i] - base)/mn*mn;
				if(sg[stones[i]-down] < 0) fail = true;
				else result ^= down/mn + sg[stones[i]-down];
			}
		}
		if(fail) puts(player2);
		else puts(result ? player1 : player2);
		//else printf("%d\n",result);
	}
	while(getchar() != EOF);
	return 0;
}

我的代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
int a[111111],b[111];
int sg[31111];
int main(){
   // freopen("4672.in","r",stdin);
    int n,m;
    char s1[20],s2[20];
    while(scanf("%d %d %s %s",&n,&m,s1,s2)!=EOF){
        for(int i=0;i<n;i++){
            scanf("%d",&a[i]);
        }
        int mm=1e9;

        memset(sg,-1,sizeof(sg));
        sg[0]=0;

        for(int i=0;i<m;i++){
            scanf("%d",b+i);
            mm=min(mm,b[i]);
            for(int j=0;j+b[i]<=10000;j++)
                if(-1!=sg[j])
                    sg[j+b[i]]=max(sg[j+b[i]],sg[j]+1);
        }
        bool fail=false;
        int ret=0;
        for(int i=0;i<n;i++){
            int t=a[i];
            if(t<=10000){
                if(sg[t]==-1){
                    fail=true;
                    break;
                }else{
                    ret^=sg[t];
                }
            }else{
                t=(t-10000)%mm+10000-mm;
                if(sg[t]==-1){
                    fail=true;
                    break;
                }else{
                    ret^=(a[i]-t)/mm + sg[t];
                }
            }
        }
        if(fail || !ret) puts(s2);
        else puts(s1);
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值