【题解】Alice ans Bob(博弈论)

题目大意:在黑板上又一个序列,每次操作可以选择一个数减1,或者是合并两个数,一个数被减至1则自动消除,不能操作者输。

解题思路:结论,对于大于1的数可以看成是一个整数s,为消除他们的总操作步数,包括减1以及合并,c为列中1的个数,如果s>2的话,c或者是s为奇数则为必胜,否则必败。若s≤2的话(s=2或者s=0)是,判断c是否为3的倍数,是的话必败,不是的话必胜。

证明:s>2时,s和c均为偶数是为必败态。

  1. s为奇数,c为偶数:先手操作,将s减1,转移至必败态。
  2. s为偶数,c为奇数:先手操作,将一个1减1,则c会减少1,转移至必败态。
  3. s为奇数,c为奇数:将一个1和s中的一个数合并。转移至必败态。
  4. s为偶数,c为偶数:s减1,则s为奇数c为偶数,必胜态;c减1,则s为偶数c为奇数,必胜态;合并1和非1数,s奇数c奇数,必胜态;

s=0者s=2时,当c%3=0时,为必败态。

  1. c%3=1时,将一个1减1,c%3==0,转移至必败态。
  2. c%3=2时,对于s=0,合并两个1,则变为s+2,c-2,此时如果s=0,则变为s=2时,c%3=0的必败态;对于s=2,将s减1,则变为s变为1,于是1的个数又增加1,所以c%3=0,必败态。
  3. c%3=0时,减掉一个1即为必胜态。
  4. 这里其实是个死循环,直至c=0
#include<cstdio>
//如果s>2的话,c或者是s为奇数则为必胜,否则必败。
//若s≤2的话(s=2或者s=0)是,判断c是否为3的倍数,是的话必败,不是的话必胜。 
//证明见我的博客 
int n,cnt,sum,T,x;
int liu() {
    
    
	if(sum>2) return (sum&1||cnt&1);
	return cnt%3;
}
int main() {
    
    
	scanf("%d",&T);
	for(int i=1;i<=T;i++) {
    
    
		sum=cnt=0;
	    scanf("%d",&n);
	    for(int j=1;j<=n;j++) {
    
    
		    scanf("%d",&x); 
		    if(x==1) cnt++;
            else sum+=x;
	    }
	    if(cnt!=n) sum+=(n-cnt-1);
	    printf("Case #%d: %s\n",i, liu() ? "Alice" : "Bob");
	}
}

模拟递归版:

dp[i][j]表示有i个石子数为1的堆数,其它堆合并再取完的步数为j。若值为1则先取者胜,为0为先取者输。
那么就有这几种情况:将某堆只能1的拿走;两堆是1的合并;把不是1的减一;把某堆是1的并到不是1的堆上

//cnt在收敛
#include<cstdio>
#include<cstring>
using namespace std;
const int MAXN=1005;
int n,cnt,sum,x,ans,dp[55][55*MAXN],T;
int dfs(int cnt,int sum) {
    
    
	if(sum==1) cnt++,sum--;//否则不满足假设(必写) 
	if(dp[cnt][sum]!=-1) return dp[cnt][sum];
	int t=0;
	if(cnt==0) {
    
    
		dp[cnt][sum]=sum%2;
		return dp[cnt][sum];
	}
	if(cnt>=1&&!dfs(cnt-1,sum)) t=1;//拿掉一个1
	else if(sum>=1&&!dfs(cnt,sum-1)) t=1;//在非1堆进行一次操作(合并或减1)
	else if(cnt>=2&&sum>=1&&!dfs(cnt-2,sum+3)) t=1;//两个1合并成一堆,原先有堆
	else if(cnt>=2&&sum==0&&!dfs(cnt-2,sum+2)) t=1;//两个1合并成一堆,原先没堆
	else if(cnt>=1&&sum>=1&&!dfs(cnt-1,sum+1)) t=1;//一个1和非1堆合并
	dp[cnt][sum]=t;
	return t;
}
int main() {
    
    
	memset(dp,-1,sizeof(dp));
	scanf("%d",&T);
	for(int i=1;i<=T;i++) {
    
    
		sum=cnt=0;
	    scanf("%d",&n);
	    for(int j=1;j<=n;j++) {
    
    
		    scanf("%d",&x); 
		    if(x==1) cnt++;
            else sum+=x;
	    }
	    if(cnt!=n) sum+=(n-cnt-1);
	    ans=dfs(cnt,sum);
	    printf("Case #%d: ",i);
	    if(ans==1) printf("Alice\n");
	    else printf("Bob\n");
	}
	return 0;
}

————————————————
原文链接:收藏思维板块里

猜你喜欢

转载自blog.csdn.net/cqbzlydd/article/details/104949834
ans