【BZOJ1413】[ZJOI2009]取石子游戲(博弈論,動態規划)


【BZOJ1413】[ZJOI2009]取石子游戲(博弈論,動態規划)

題面

BZOJ
洛谷

題解

神仙題.jpg。\(ZJOI\)是真的神仙。
發現\(SG\)函數等東西完全找不到規律,無奈只能翻題解。

首先設\(L[i][j]\)表示在\([i,j]\)這一段區間的左側放上一堆數量為\(L[i][j]\)的石子后,先手必敗。同理定義\(R[i][j]\)表示右側。
首先我們可以證明\(L[i][j]\)唯一,假設存在兩個\(L[i][j]\),顯然較大的那個可以通過一步轉移轉移到較小的那個,所以不合法。因此\(L[i][j]\)唯一。
接下來考慮如何證明\(L[i][j]\)一定存在。假設\(L[i][j]\)不存在,那么對於這段區間而言,在左邊加上任意一堆石子先手都必勝,既然先手必勝意味着先手進行一步操作之后可以到達一個必敗態,這里分情況討論。假設先手拿的是最左邊的一堆石子,因為不存在\(L[i][j]\),所以只要拿了左邊的石子之后,當前局面都是必勝態,所以不可能拿左邊的石子。那么只能拿右邊的石子,那么無論右邊拿了一定量之后,無論左邊添加了多少,都是一個必敗態,那么此時后手在左側隨便拿走一定數量,這個狀態也還是一個必敗態,顯然也不成立。因此\(L[i][j]\)必定存在。
綜上,我們知道了\(L[i][j]\)一定存在並且唯一,而\(L[][],R[][]\)顯然是對稱的,因此\(R[i][j]\)也滿足上述性質。
現在考慮如何求解\(L[i][j]\),\(R[i][j]\)同理。首先邊界情況顯然,\(L[i][i]=a[i]\),因為只剩下兩堆一模一樣的情況的時候,后手只需要模仿先手的行動對稱執行就好了,這樣子一定不會輸,即先手必敗。
接下來來大力分類討論,為了方便(為了抄),設\(L=L[i][j-1],R=R[i][j-1],x=a[j]\)

  • \(x=R\)
    這種情況下顯然只需要直接把\(a[j]\)放進去就好了,即這個區間本身就是一個必敗態。所以\(L[i][j]=0\)
  • \(x<L,x<R\)
    這種情況下\(L[i][j]=x\)。這種情況下最靠左的\(L[i][j]\)\(x=a[j]\)是相同的,意味着先手無論怎么取,后手顯然可以學着它的方法取,也就意味着左右兩堆中顯然必然會先拿完一堆,此時后手學着拿的那一堆的石子數一定也是小於\(L,R\)的。假設先手先拿完了最靠右的一堆,即剩下了\([i,j-1]\),因為\(L[i][j-1]\)表示的是在這一段區間最左側加入一個\(L[i][j-1]\)的堆,無論先手怎么取先手都是必敗的,那么我們等價的認為先手取走了這一堆的一部分,顯然后手是必勝的。假如先手先取完的是最左的一堆,同理,\(R[i][j-1]\)的含義是在最右側加入了一堆,而\(a[j]<R[i][j-1]\),我們還是可以等價的認為先手在這一堆中取走了若干石子,而這個狀態對於先手而言是必敗狀態,因此顯然后手必勝。
  • \(R<x<L\)
    這種情況下\(L[i][j]=x-1\)。這樣子考慮,假設先手先拿了左邊這一堆,那么假設還剩下了\(z\)個石子,如果\(z<R\),后手把右側的那一堆也給拿成\(z\)就變成了上面的情況。如果\(z\ge R\),那么后手把最后那一堆拿成\(z+1\),於是又回到了這種情況,相當於這種情況遞歸處理。如果先手先拿的是右側的這一堆,還是一樣的,假設把它拿成了\(z\),如果\(z<R\),同上可以變成\(x<L,x<R\)的情況;如果\(y=R\),直接把左邊拿完,就變成了\(R[i][j-1]\)的定義了,先手必敗;如果\(z>R\),把左邊那堆變成\(z-1\),同樣遞歸處理。
  • \(L<x<R\)
    分析同上,\(L[i][j]=x+1\)
  • \(x>L,x>R\)
    \(L[i][j]=x\)。還是一樣的,假設先手把其中一堆拿成了\(z\)。如果\(z>L,R\),跟着先手拿成一樣多的石子則又回到了這種情況。如果\(z<L,R\),則可以回到情況\(x<L,R\)。否則的話對應着把另外一堆變成\(z+1\)或者\(z-1\),對應着\(L<x<R\)\(R<x<L\)兩種情況。

\(R[][]\)\(L[][]\)是對稱的,類似的求解即可。
那么最終只需要判斷\(L[2][n]\)\(a[1]\)是否相等即可判斷勝負情況。
代碼有點丑

#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 1010
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
int n,a[MAX],L[MAX][MAX],R[MAX][MAX];
int main()
{
	int T=read();
	while(T--)
	{
		n=read();
		for(int i=1;i<=n;++i)a[i]=read();
		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==l)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");
	}
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM