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;
}