1. 程式人生 > 實用技巧 >P4363 [九省聯考2018]一雙木棋chess 狀壓DP

P4363 [九省聯考2018]一雙木棋chess 狀壓DP

題面:

戳這裡

分析:

  • 暴力:

暴搜出每種狀態下每個人的最優決策,期望得分:\(30pts\)

  • 正解

我們通過模擬可以發現,最終選擇出來的狀態一定是呈現出梯形的樣子,所以我們聯想到針對形狀的 輪廓線DP,我們規定橫邊用 \(0\) 表示,豎邊用 \(1\) 表示,每一個狀態從右上到左下可表示為一組長度為 \(n+m-2\)\(01\) 序列

序列的初始值是 \(0\dots(m-1個)\ 1\dots(n-1個)\),結束值是 \(1\dots(n-1個)\ 0\dots(m-1個)\)

每一次狀態轉移相當於把一對 \(01\to10\)

複雜度是 \(O(2^{n+m-2}\log)\)

  • 另解

對於一人求局面最小值,一人求局面最大值,典型的 \(min-max\) 搜尋,但是單純的 \(min-max\) 搜尋複雜度不對,需要進行 \(\alpha-\beta\) 剪枝 (日後再填坑吧)

程式碼:

#include<bits/stdc++.h>

using namespace std;

namespace zzc
{
	int read()
	{
		int x=0,f=1;char ch=getchar();
		while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
		while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
		return x*f;
	}
	
	const int maxn = 10;
	const int maxm = (1<<(maxn<<1));
	const int inf = 1e9+7;
	bool vis[maxm];
	int f[maxm],a[maxn][maxn],b[maxn][maxn];
	int n,m;
	
	int dfs(int st,int who)
	{
		if(vis[st]) return f[st];
		f[st]=who?-inf:inf;
		int x=n,y=0;
		for(int i=0;i<n+m-1;i++)
		{
			if((st>>i)&1) x--;
			else y++;
			if((st>>i&3)!=1) continue;
			int to=st^(3<<i);
			if(who) f[st]=max(f[st],dfs(to,who^1)+a[x][y]);
			else f[st]=min(f[st],dfs(to,who^1)-b[x][y]);
		}
		vis[st]=true;
		return f[st];
	}
	
	void work()
	{
		n=read();m=read();
		for(int i=0;i<n;i++) for(int j=0;j<m;j++) a[i][j]=read();
		for(int i=0;i<n;i++) for(int j=0;j<m;j++) b[i][j]=read();
		f[((1<<n)-1)<<m]=0;
		vis[((1<<n)-1)<<m]=true;
		printf("%d\n",dfs((1<<n)-1,1));
	}
	
	
}

int main()
{
	zzc::work();
	return 0;
}