題目連接:https://loj.ac/problem/10248#submit_code
解題思路
神仙題目,迷通常的狀態轉移,看了yyb大佬的題解,纔算明白一點。咱們定義L[i][j]表示區間[i,j]左邊(即i-1位)加上一堆石子L[i][j],能使先手必敗,R[i][j]表示區間[i,j]右邊(即j+1位)加上一堆石子R[i][j],能使先手必敗。首先,邊界L[i][i],R[i][i]確定是a[i] (Nim博弈)。而後最後只要判斷a[1]是否等於L[2][n]就能判斷先手必勝仍是必敗了。
再看狀態轉移,L[i][j]由L[i][j-1]與R[i][j-1]轉移過來,R[i][j]由R[i+1][j],L[i+1][j]轉移過來,先看L[i][j],咱們設L=L[i][j-1],R[i][j-1],x=a[j]。
ios
- R=x,L[i][j]=0,由於此時已是必敗態了,沒必要再添了。
- x<L&&x<R,L[i][j]=x,此時兩側石子一致,不管先手怎麼去,後手只要模仿先手在另外一側取相同的數量,這樣就能保證先手先取完,此時左側或右側確定有一堆沒取完,此時能夠等價的認爲,當前是先手從L或R取了必定數量的石子轉移過來的,此時不管後手怎麼取都是必勝的。
- L<x<R,L[i][j]=x+1,若先手在左側取,剩下石子爲rest。rest=0,等價於先手在R的狀態下,在右側取了必定數量的石子轉移過來。rest<L,後手只要將右側石頭取到與rest相同,就能夠轉移到第2種狀況。rest=L,後手將右側石子取完,就轉移到L。rest>L,後手只要將右手石子取到rest-1即又回到當前狀況。若先手在右側取,當rest=0,將左側取出L,便可轉移到L。當rest<L,同上,取左側,能夠轉移到第2種狀況。rest>=L,後手經過在左側或右側,使得左側=右側+1,又回到當前狀況。
- R<x<L,L[i][j]=x-1,與第三種狀況相似,能夠當作互換一下左右兩側。
- 當x>L&&x>R,L[i][j]=x。不管先手怎麼取,後手均可以轉移到以前和當前的狀態。
R[i][j]和L[i][j]是對稱的,相似的求解便可。url
AC代碼spa
#include <iostream> #include <stdio.h> #include <cstring> #include <algorithm> using namespace std; const int maxn=1e3+5; int a[maxn],L[maxn][maxn],R[maxn][maxn]; int T,n; int main() { scanf("%d",&T); while(T--) { scanf("%d",&n); for(int i=1;i<=n;++i) scanf("%d",&a[i]); for(int i=1;i<=n;++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],l=L[i][j-1],r=R[i][j-1]; if(x==r) L[i][j]=0; else if((x>l&&x>r)||(x<l&&x<r)) L[i][j]=x; else if(r<x&&x<l) L[i][j]=x-1; else L[i][j]=x+1; x=a[i],l=L[i+1][j],r=R[i+1][j]; if(x==1) R[i][j]=0; else if((x>l&&x>r)||(x<l&&x<r)) R[i][j]=x; else if(r<x&&x<l) R[i][j]=x+1; else R[i][j]=x-1; } puts(a[1]==L[2][n]?"0":"1"); } return 0; }