#轮廓线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<