【Luogu】P2599取石子游戏(博弈论)
情况非常复杂,事实上题解我现在也没有完全理解
不过大致的意思就是
设两个数组lef[][],rig[][]表示对应区间左端加一堆数量为lef[][]的石子使得先手必败,rig同理
可以通过一堆证明证明求出来的值具有唯一性
所以最后需要判断lef[2][n]是不是等于a[1]。
对于一段区间[i,j]我们设L=lef[i][j-1],R=rig[i][j-1],X=a[j]
据说lef[i][j]只跟这三个数有关
情况大致分以下五种
1:R=X,此时lef[i][j]=0,因为此时[i,j]已经必败了,左边瞎搞就好
2:X<L&&X<R,此时lef[i][j]=X,因为这样左右两边就能对齐,然后后手占据主动,然后递归。
3:X<L&&X>R,此时lef[i][j]=X-1,因为这时候先手在左边拿,后手从右边xjb拿成同样结果就行;如果先手在右边拿,后手从左边拿完全可以绕回当前的局势来,如果先手把右边拿到R,后手直接把lef[][]加的那堆石子清空,先手就GG了
4:X>=L&&X<R,跟4完全相反
5:X>L&&X>R,lef[i][j]=x。
rig[][]求法跟lef[][]对称。
#include<cstdio> #include<algorithm> #include<cstdlib> #include<cstring> #include<cctype> #define maxn 2020 using namespace std; inline long long read(){ long long num=0,f=1; char ch=getchar(); while(!isdigit(ch)){ if(ch=='-') f=-1; ch=getchar(); } while(isdigit(ch)){ num=num*10+ch-'0'; ch=getchar(); } return num*f; } int lef[maxn][maxn]; int rig[maxn][maxn]; int main(){ int T=read(); while(T--){ int n=read(); for(int i=1;i<=n;++i) lef[i][i]=rig[i][i]=read(); for(int len=2;len<n;++len) for(int i=1;i+len-1<=n;++i){ int j=i+len-1; int L=lef[i][j-1],R=rig[i][j-1],x=lef[j][j]; if(R==x) lef[i][j]=0; else if(x<L&&x<R) lef[i][j]=x; else if(x<L&&x>R) lef[i][j]=x-1; else if(x<R&&x>=L) lef[i][j]=x+1; else if(x>L&&x>R) lef[i][j]=x; L=rig[i+1][j],R=rig[i+1][j],x=lef[i][i]; if(L==x) rig[i][j]=0; else if(x<L&&x<R) rig[i][j]=x; else if(x>L&&x<R) rig[i][j]=x-1; else if(x<L&&x>=R) rig[i][j]=x+1; else if(x>L&&x>R) rig[i][j]=x; } if(lef[2][n]==lef[1][1]) printf("0\n"); else printf("1\n"); } return 0; }