#轮廓线dp,博弈论#洛谷 4363 [九省联考 2018] 一双木棋 chess


题目传送门


分析

菲菲想让答案尽量大,牛牛想让答案尽量小。

很天真的一种想法就是设 \(dp[i][j]\) 表示现在选择 \((i,j)\) 的答案。

但是这样有一个弊端就是并不知道其它位置怎么选择。

准确来说,已经被选择的位置和未被选择的位置存在一条分割线,或者直接叫轮廓线。

设横着的轮廓表示0,竖着的轮廓表示1,那么一开始的轮廓线从左下往右上看就是 \(n\)\(1\)\(m\)\(0\)

然后每次选择的位置就是形如 \(\dots10\dots\)\(0\) 所在的位置,每次选择一个合适的位置将这个数算进贡献中。

可以发现每种轮廓线只能被其中一个人选择,转移大概就是如果是菲菲操作,那么加 \(a_{x,y}\) 取最大值,否则牛牛减 \(b_{x,y}\) 取最小值。

那么这样可用的状态实则为 \(\binom{n+m}{n}\) 个,记忆化一下就可以了。


代码

#include 
#include 
using namespace std;
const int _inf=0xcfcfcfcf;
int dp[1<<20],n,m,a[11][11],b[11][11];
void Min(int &x,int y){x=xy?x:y;}
int dfs(int S,int opt){
	if (dp[S]!=_inf) return dp[S];
	if (opt) dp[S]=-dp[S];
	int x=1,y=m;
	for (int i=0;i<(n+m-1);++i){
		if (((S>>i)&3)==2){
			int _S=S^(3<>i)&1) ++x;
		    else --y;
	}
	return dp[S];
}
int main(){
	ios::sync_with_stdio(0);
	cin>>n>>m;
	for (int i=1;i<=n;++i)
	for (int j=1;j<=m;++j) cin>>a[i][j];
	for (int i=1;i<=n;++i)
	for (int j=1;j<=m;++j) cin>>b[i][j];
	memset(dp,0xcf,sizeof(dp));
	dp[(1<

相关