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) 和等于一的 §
于是我们得到了以下操作方式:
-
在P中取一个
-
在Q中取一个
-
在P内部合并
-
在Q内部合并
-
将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;
}
真的…博弈论关键考察思维能力…