BZOJ 5248: [2018多省省队联测]一双木棋
题目大意:
一个网格,两个人轮流放棋子,一个格子能放当且仅当这个格子的左面和上面已经填满。两个人每放一个格子都会获得相应的得分。
目标:自己得分-对方得分尽可能大。
问最终得分差。
题解:
状压DP,用0/1来表示轮廓线。
代码:
#include<cstdio> #include<algorithm> using namespace std; int n,m,f[1<<20][2],a[15][15],b[15][15]; int dfs(int s,int cas){ if (cas==0 && f[s][cas]!=-1e9) return f[s][cas]; else if (cas==1 && f[s][cas]!=1e9) return f[s][cas]; int ans; if (cas==0) ans=-1e9; else ans=1e9; int x=n+1,y=1; if (s&(1<<(n+m-1))) x--; else y++; for (int i=2; i<=n+m; i++){ int pre=s&(1<<(n+m-i+1)); int now=s&(1<<(n+m-i)); if (pre && !now){ int to=s-(1<<(n+m-i+1))+(1<<(n+m-i)); if (cas==0) ans=max(ans,a[x][y]+dfs(to,1)); else ans=min(ans,dfs(to,0)-b[x][y]); } if (now) x--; else y++; } f[s][cas]=ans; return ans; } int main(){ scanf("%d%d",&n,&m); for (int i=1; i<=n; i++) for (int j=1; j<=m; j++) scanf("%d",&a[i][j]); for (int i=1; i<=n; i++) for (int j=1; j<=m; j++) scanf("%d",&b[i][j]); int s=0; for (int i=1; i<=n; i++){ s=s<<1; s|=1; } for (int i=1; i<=m; i++) s=s<<1; for (int i=0; i<(1<<n+m); i++) f[i][0]=-1e9,f[i][1]=1e9; int t=0; for (int i=1; i<=n; i++){ t=t<<1; t|=1; } f[t][0]=f[t][1]=0; printf("%d\n",dfs(s,0)); return 0; }