洛谷2953 [USACO09OPEN]牛的数字游戏Cow Digit Game(博弈)

题目

贝茜和约翰在玩一个数字游戏.贝茜需要你帮助她。
游戏一共进行了G(1≤G≤100)场。第i场游戏开始于一个正整数Ni(l≤Ni≤1,000,000)。游戏规则是这样的:双方轮流操作,将当前的数字减去一个数,这个数可以是当前数字的最大数码,也可以是最小的非0数码.比如当前的数是3014,操作者可以减去1变成3013,也可以减去4变成3010。若干次操作之后,这个数字会变成0.这时候不能再操作的一方为输家。
贝茜总是先开始操作.如果贝茜和约翰都足够聪明,执行最好的策略.请你计算最后的赢家。


题解1

博弈SG函数
公式sg[i]=mex{ sg[j] }(要求i->j,j是i的后继情况)
易知0是先手为负,令sg[0]=0。
以后的sg[i]=mex{ sg[i-mx],sg[i-mn] }(其中mx,mn,分别是最大,最小数码)
预处理所有sg后O(1)回答问题。


代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1000010;


int sg[maxn];//sg值的范围0~2 


bool v[10];
int mex()
{
	for(int i=0;;i++)
	{
		if(!v[i]) return i;
	}
}


void get_sg(int q)
{
	sg[0]=0;
	for(int i=1;i<=q;i++)
	{
		int j=i,mx=-1,mn=10;
		while(j>0)
		{
			if(j%10!=0) mx=max(mx,j%10),mn=min(mn,j%10);
			j/=10;
		}
		memset(v,false,sizeof(v));
		if(mx!=-1) v[sg[i-mx]]=true;
		if(mn!=10) v[sg[i-mn]]=true;
		sg[i]=mex();
	}
}


int main()
{
	get_sg(1000000);
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		int x;
		scanf("%d",&x);
		if(sg[x]==0) printf("NO\n");//sg=0,先手必败 
		else printf("YES\n");//sg>0,先手必胜 
	}
	return 0;
}


题解2

博弈SG函数
这回码的sg就不是真正的sg了,因为这题中sg只在乎0和大于0,干脆分别用0和1表示。
这次sg的求值不用公式了,根据其原理来求:
若当前状态的子状态中有必败状态,则该状态为必胜状态;
若当前状态的子状态中无必败状态(即都是必胜状态),则该状态为必败状态。


代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1000010;


int sg[maxn];//对于数字为i能赢为1,否则为0 


int dfs(int q)
{
	if(sg[q]!=-1) return sg[q];
	int i=q,mx=-1,mn=10;
	while(i>0)
	{
		if(i%10!=0) mx=max(mx,i%10),mn=min(mn,i%10);
		i/=10;
	}
	sg[q]=0;//有1个为0则sg为1,全都不为0则sg为0 
	if(mx!=-1)
		if(dfs(q-mx)==0) sg[q]=1;
	if(mn!=9 ) 
		if(dfs(q-mn)==0) sg[q]=1;
	return sg[q];
}


int main()
{
	memset(sg,-1,sizeof(sg));sg[0]=0;
	dfs(1000000);
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		int x;
		scanf("%d",&x);
		if(sg[x]==0) printf("NO\n");
		else printf("YES\n");
	}
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值