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;
} 
posted @ 2020-12-26 19:42  youth518  阅读(69)  评论(0编辑  收藏  举报