Description
有\(n\)堆石子,将这\(n\)堆石子摆成一排。游戏由两个人进行,两人轮流操作,每次操作者都可以从最左或最右的一堆中取出若干颗石子,可以将那一堆全部取掉,但不能不取,不能操作的人就输了。
Solution
博弈+区间DP.
这道题好厉害啊qwq...
首先一个区间\([l,r]\)在左边或右边添加一个数是个\(P\)一定仅有一个。
因为有另一个的话,从另一个可以转移到当前这个或者从这个可以转移到另一个\((x=y+\Delta)\),所以仅存在一个。
有了这个性质,可以考虑递推。
设\(L[i][j]\)表示在区间\([i,j]\)左边添加\(L[i][j]\)是\(P\)态,\(R[i][j]\)表示在区间\([i,j]\)右边添加\(R[i][j]\)是\(P\)态。
从\([i,j-1]\)来计算\(L[i][j]\),为了表示方便令\(x=L[i][j-1],y=R[i][j-1],z=a[j]\).
1. 若\(y=z\)这个区间是必败的,所以\(L[i][j]=0\)。
2. 若\(x,y<z || z<x,y\),那么\(L[i][j]=z\),因为取走左右一堆的是必败的,所以后手只需要和先手相同即可。
3. 若\(x\leqslant z<y\),那么\(L[i][j]=z+1\),以为如果取到\(x\),后手取到\(x+1\),若取到\(x+1\),后手取到\(x+2\),若小于\(x\),后手只需要保证相等即可。
4. 若\(y<z\leqslant x\),跟上边类似\(L[i][j]=z-1\)。
这道题主要就是在考虑后手的操作。
Code
/**************************************************************
Problem: 1413
User: BeiYu
Language: C++
Result: Accepted
Time:204 ms
Memory:9904 kb
****************************************************************/
#include <bits/stdc++.h>
using namespace std;
const int N = 1050;
inline int in(int x=0,char ch=getchar()) { while(ch>'9' || ch<'0') ch=getchar();
while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();return x; }
int T,n;
int a[N];
int L[N][N],R[N][N];
int main() {
for(T=in();T--;) {
n=in();
for(int i=1;i<=n;i++) a[i]=in(),L[i][i]=R[i][i]=a[i];
for(int i=n;~i;--i) for(int j=i+1;j<=n;j++) {
int x=L[i][j-1],y=R[i][j-1],z=a[j];
if(y==z) L[i][j]=0;
else {
if(z<x && z<y) L[i][j]=z;
else if(x<=z && z<y) L[i][j]=z+1;
else if(y<z && z<=x) L[i][j]=z-1;
else if(x<z && y<z) L[i][j]=z;
}
x=R[i+1][j],y=L[i+1][j],z=a[i];
if(y==z) R[i][j]=0;
else {
if(z<x && z<y) R[i][j]=z;
else if(x<=z && z<y) R[i][j]=z+1;
else if(y<z && z<=x) R[i][j]=z-1;
else if(x<z && y<z) R[i][j]=z;
}
}
printf("%d\n",(n==1||a[1]!=L[2][n]));
}return 0;
}