【博弈论】SRM701PartisanGame

题意:有n个石子,Alice和Bob依次拿石子,到一方不能再拿即为失败。Alice能够拿的个数有x种,分别是a0,a1…ax。Bob能够拿的个数有y种,分别是b0,b1…bx。Alice先手
1<=n<=1000000000
x<=5,y<=5且1<=ai,bi<=5,ai之间互不相同,bi之间也互不相同


题解:
这道题很不同寻常的是,两者拿的个数种类不一定相同,这也意味着不能单纯地用一维的dp来实现。考虑加维度,显然,我们的目的是区分开当前到底是谁拿,所以第二维为0/1分别表示当前轮到Alice或Bob出手。时间复杂度O(n),但很显然这样并不能过这道题。
所以我们考虑周期性
“x,y<=5且ai,bi<=5”很显然就是突破口,一种情况d[i][x]的答案只会从前5个(d[i-5][!x]–d[i-1][!x])的情况得到。那么不难发现对一种情况有影响的只是前5个情况,所以就可以用前5个的情况来储存当前状态。状态相同即一定为一个周期,用一个十位二进制数来存储,所以不同的状态总数为210个,这样就可以把循环的次数降到210以下。(详见代码)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<climits>
#define SF scanf
#define PF printf
#define MAXS 10
#define MAXN 1000000
using namespace std;
int a[MAXS],b[MAXS],d[MAXN][2],was[(1<<(11))+10],n,x,y;
int main(){
	memset(was,-1,sizeof was);
	SF("%d",&n);
	SF("%d",&x);
	for(int i=1;i<=x;i++)
		SF("%d",&a[i]);
	SF("%d",&y);
	for(int i=1;i<=y;i++)
		SF("%d",&b[i]);
    sort(a+1,a+x+1);
    sort(b+1,b+y+1);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=x&&i-a[j]>=0;j++)
			if(d[i-a[j]][0]==0){
				d[i][1]=1;
				break;
			}
		for(int j=1;j<=y&&i-b[j]>=0;j++)
			if(d[i-b[j]][1]==0){
				d[i][0]=1;
				break;
			}
		if(i>5){
			int m=0;
			for(int j=0;j<5;j++)
				for(int k=0;k<2;k++){
					m*=2;
					m+=d[i-j][k];
				}
			if(was[m]==-1)
				was[m]=i;
			else{
				int p=i-was[m];
				n-=(n-i)/p*p;
			}
		}
	}
	if(d[n][1])
		PF("Alice");
	else
		PF("Bob");
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值