AcWing1321取石子

题意

A ( A l i c e ) A(Alice) A(Alice) B ( B o b ) B(Bob) B(Bob)在玩取石子游戏

N N N堆石子排成一排,A和B轮流进行如下操作(A是先手):

  • 从某堆石子中取走一个
  • 合并任意两堆石子

无法进行操作的人输

让你判断A是否有必胜策略

有多组测试数据哦

思路

假设:我们当前所有堆得石子个数都严格大于1

我们令 b=堆数+石子总数-1

此时先手必胜 ⟺ \Longleftrightarrow b是奇数

为什么呢?

每个操作一会使石子总数减一

二每个操作二会使石子堆数减一

因此b就是操作总数(最后只剩一堆石子,且这堆石子中只有一个石子)

如果b是奇数,一定存在一个偶数后继

我们便把这个偶数后继留给对手,直到对手无路可走

而如果b是偶数,所有后继必然是奇数

所以此时先手必胜

对于b是偶数的情况,若采取合并措施,则b变为奇数

若从某一堆中去一个石子

如果取后这个堆得石子个数大于1,那么b变为奇数

否则再进行讨论.若这一堆是最后一堆,那么显然必败

如果这一堆不是最后一堆,那么可以把剩下的石子合并到其他堆中,b也变成奇数

因此结论成立

再看一般情况

考虑暴力求解

我们假设 石子个数=1 记为 a

f(a,b)表示有a个石子个数为1的堆,操作步数为b是否必胜

b的定义不变

我们把堆分成两部分,石子个数大于1的 (Q) 和等于一的 §

于是我们得到了以下操作方式:

  1. 在P中取一个

  2. 在Q中取一个

  3. 在P内部合并

  4. 在Q内部合并

  5. 将P中的一堆和Q中的一堆进行合并

操作一:f(a-1,b)

操作二:f(a,b-1)

操作三:f(a-2,b+3)

操作四:f(a,b-1)

操作五:f(a-1,b+1)

对于以上操作,若有必败态,则f(a,b)必胜否则他必败

复杂度 O ( n 2 ) O(n^2) O(n2)

/*************************************************************************
    > File Name: p1321取石子.cpp
    > Author: typedef
    > Mail: 1815979752@qq.com 
    > Created Time: 2020/12/3 19:16:10
 ************************************************************************/
#include<bits/stdc++.h>
using namespace std;
const int N=55,M=50050;
int f[N][M];
int dp(int a,int b){
	int &v=f[a][b];
	if(v!=-1) return v;
	if(!a) return v=b%2;
	if(b==1) return dp(a+1,0);
	//以上是边界
	if(a&&!dp(a-1,b)) return v=1;
	if(b&&!dp(a,b-1)) return v=1;
	if(a>=2&&!dp(a-2,b+(b?3:2))) return v=1;
	if(a&&b&&!dp(a-1,b+1)) return v=1;
	return v=0;
}
int main(){
	memset(f,-1,sizeof(f));
	int T;
	scanf("%d",&T);
	while(T--){
		int n;
		scanf("%d",&n);
		int a=0,b=0;
		for(int i=1;i<=n;i++){
			int x;
			scanf("%d",&x);
			if(x==1) a++;
			else b+=b?x+1:x;
		}
		if(dp(a,b)) puts("YES");
		else puts("NO");
	}
	system("pause");
	return 0;
}

真的…博弈论关键考察思维能力…

posted @ 2020-12-03 21:14  actypedef  阅读(31)  评论(0编辑  收藏  举报