BZOJ 1413: [ZJOI2009]取石子游戏
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; }