#輪廓線dp,博弈論#洛谷 4363 [九省聯考 2018] 一雙木棋 chess
阿新 • • 發佈:2022-03-23
分析
菲菲想讓答案儘量大,牛牛想讓答案儘量小。
很天真的一種想法就是設 \(dp[i][j]\) 表示現在選擇 \((i,j)\) 的答案。
但是這樣有一個弊端就是並不知道其它位置怎麼選擇。
準確來說,已經被選擇的位置和未被選擇的位置存在一條分割線,或者直接叫輪廓線。
設橫著的輪廓表示0,豎著的輪廓表示1,那麼一開始的輪廓線從左下往右上看就是 \(n\) 個 \(1\) 和 \(m\) 個 \(0\)。
然後每次選擇的位置就是形如 \(\dots10\dots\) 的 \(0\) 所在的位置,每次選擇一個合適的位置將這個數算進貢獻中。
可以發現每種輪廓線只能被其中一個人選擇,轉移大概就是如果是菲菲操作,那麼加 \(a_{x,y}\)
那麼這樣可用的狀態實則為 \(\binom{n+m}{n}\) 個,記憶化一下就可以了。
程式碼
#include <iostream> #include <cstring> 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=x<y?x:y;} void Max(int &x,int y){x=x>y?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); if (!opt) Max(dp[S],dfs(_S,1)+a[x][y]); else Min(dp[S],dfs(_S,0)-b[x][y]); } if ((S>>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<<n)-1]=0; cout<<dfs(((1<<n)-1)<<m,0); return 0; }