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. 若(xleqslant z<y),那么(L[i][j]=z+1),以为如果取到(x),后手取到(x+1),若取到(x+1),后手取到(x+2),若小于(x),后手只需要保证相等即可。
4. 若(y<zleqslant 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==1||a[1]!=L[2][n])); }return 0; }