P2599 [ZJOI2009]取石子游戏
Posted mysh
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P2599 [ZJOI2009]取石子游戏相关的知识,希望对你有一定的参考价值。
题目描述
在研究过Nim游戏及各种变种之后,Orez又发现了一种全新的取石子游戏,这个游戏是这样的: 有n堆石子,将这n堆石子摆成一排。游戏由两个人进行,两人轮流操作,每次操作者都可以从最左或最右的一堆中取出若干颗石子,可以将那一堆全部取掉,但不能不取,不能操作的人就输了。 Orez问:对于任意给出一个初始一个局面,是否存在先手必胜策略。
输入格式
文件的第一行为一个整数T,表示有 T组测试数据。对于每组测试数据,第一行为一个整数n,表示有n堆石子;第二行为n个整数ai,依次表示每堆石子的数目。
输出格式
对于每组测试数据仅输出一个整数0或1。其中1表示有先手必胜策略,0表示没有。
输入输出样例
1 4 3 1 9 4
0
说明/提示
数据范围
对于30%的数据 n≤5 ai≤10^5
对于100%的数据 T≤10 n≤1000 每堆的石子数目≤10^9
思路
By :yybyyb
发现SG函数等东西完全找不到规律,无奈只能翻题解。
首先设L[i][j]表示在[i,j]这一段区间的左侧放上一堆数量为L[i][j]的石子后,先手必败。同理定义R[i][j]表示右侧。
首先我们可以证明L[i][j]唯一,假设存在两个L[i][j],显然较大的那个可以通过一步转移转移到较小的那个,所以不合法。因此L[i][j]唯一。
接下来考虑如何证明L[i][j]一定存在。假设L[i][j]不存在,那么对于这段区间而言,在左边加上任意一堆石子先手都必胜,既然先手必胜意味着先手进行一步操作之后可以到达一个必败态,这里分情况讨论。假设先手拿的是最左边的一堆石子,因为不存在L[i][j],所以只要拿了左边的石子之后,当前局面都是必胜态,所以不可能拿左边的石子。那么只能拿右边的石子,那么无论右边拿了一定量之后,无论左边添加了多少,都是一个必败态,那么此时后手在左侧随便拿走一定数量,这个状态也还是一个必败态,显然也不成立。因此L[i][j]必定存在。
综上,我们知道了L[i][j]一定存在并且唯一,而L[][],R[][]显然是对称的,因此R[i][j]也满足上述性质。
现在考虑如何求解L[i][j],R[i][j]同理。首先边界情况显然,L[i][i]=a[i],因为只剩下两堆一模一样的情况的时候,后手只需要模仿先手的行动对称执行就好了,这样子一定不会输,即先手必败。
接下来来大力分类讨论,为了方便,设L=L[i][j−1],R=R[i][j−1],x=a[j]
-
x=R
这种情况下显然只需要直接把a[j]放进去就好了,即这个区间本身就是一个必败态。所以L[i][j]=0。
-
x<L,x<R
这种情况下L[i][j]=x。这种情况下最靠左的L[i][j]和x=a[j]是相同的,意味着先手无论怎么取,后手显然可以学着它的方法取,也就意味着左右两堆中显然必然会先拿完一堆,此时后手学着拿的那一堆的石子数一定也是小于L,R的。假设先手先拿完了最靠右的一堆,即剩下了[i,j−1],因为L[i][j−1]表示的是在这一段区间最左侧加入一个L[i][j−1的堆,无论先手怎么取先手都是必败的,那么我们等价的认为先手取走了这一堆的一部分,显然后手是必胜的。假如先手先取完的是最左的一堆,同理,R[i][j−1]的含义是在最右侧加入了一堆,而a[j]<R[i][j−1],我们还是可以等价的认为先手在这一堆中取走了若干石子,而这个状态对于先手而言是必败状态,因此显然后手必胜。
-
R<x<L
这种情况下L[i][j]=x−1。这样子考虑,假设先手先拿了左边这一堆,那么假设还剩下了z个石子,如果z<R,后手把右侧的那一堆也给拿成z就变成了上面的情况。如果z≥R,那么后手把最后那一堆拿成z+1,于是又回到了这种情况,相当于这种情况递归处理。如果先手先拿的是右侧的这一堆,还是一样的,假设把它拿成了z,如果z<R,同上可以变成x<L,x<R的情况;如果y=R,直接把左边拿完,就变成了R[i][j−1]的定义了,先手必败;如果z>R,把左边那堆变成z−1,同样递归处理。
-
L<x<R
分析同上,L[i][j]=x+1。
-
x>L,x>R
L[i][j]=x。还是一样的,假设先手把其中一堆拿成了z。如果z>L,R,跟着先手拿成一样多的石子则又回到了这种情况。如果z<L,R,则可以回到情况x<L,R。否则的话对应着把另外一堆变成z+1或者z−1,对应着L<x<R和R<x<L两种情况。
而R[][]和L[][]是对称的,类似的求解即可。
那么最终只需要判断L[2][n]和a[1]是否相等即可判断胜负情况。
代码
#include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int N=1010; int t,n,a[N]; int l[N][N],r[N][N]; int main () { scanf("%d",&t); while(t--) { scanf("%d",&n); for(int i=1; i<=n; i++) { scanf("%d",&a[i]); l[i][i]=r[i][i]=a[i]; } for(int len=2; len<=n; len++) for(int i=1,j=i+len-1; j<=n; i++,j++) { int x=a[j],ll=l[i][j-1],rr=r[i][j-1]; if(x==rr) l[i][j]=0; else if((x>rr&&x>ll)||(x<ll&&x<rr)) l[i][j]=x; else if(rr<x&&x<ll) l[i][j]=x-1; else l[i][j]=x+1; x=a[i],ll=l[i+1][j],rr=r[i+1][j]; if(x==ll) r[i][j]=0; else if((x>rr&&x>ll)||(x<ll&&x<rr)) r[i][j]=x; else if(rr<x&&x<ll) r[i][j]=x+1; else r[i][j]=x-1; } if(a[1]==l[2][n]) printf("0 "); else printf("1 "); } return 0; }
以上是关于P2599 [ZJOI2009]取石子游戏的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ 1874: [BeiJing2009 WinterCamp]取石子游戏 [Nim游戏 SG函数]