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

浙公网安备 33010602011771号