【LuoguP4363】一双木棋【状压DP】
链接
题目大意:
一个n*m的网格,每个格子有a,b两个元素。两个人执黑白棋轮流下,要求每次落子点左和上必须有棋子。最后黑棋的总和是黑棋落子点的a总和,白棋为b总和,双方都采取最大化自己与对手得分差距的策略,求最后的得分差距。
思路:
n,m只有10,直接考虑状压。
这种斜角堆叠的网格很容易使用轮廓线,一开始设m个0,在当前行有多少个棋子数就从右到左数多少个0将一个1插入在后面。
然后考虑状态转移,就是将一个状态的01变成10得到前驱状态,然后用记搜。
然后以后的状压(轮廓线)不要用映射的方法了,这会导致拓扑序和顺序混乱。
code
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int MAXN = 21, inf = 1e9;
int n, m;
int a[MAXN][MAXN], b[MAXN][MAXN];
int f[1 << MAXN];
int dfs(int i, bool flag) {
if(f[i] != -1044266559)
return f[i];
if(flag) f[i] = -inf;
else f[i] = inf;
int x = n + 1, y = 1;
for (int p = 0; p < n + m - 1; p ++) {
if(i >> p & 1) x --;
else y ++;
if((i >> p & 3) != 1) continue;
int j = i ^ 3 << p;
if(flag)
f[i] = max(f[i], dfs(j, flag ^ 1) + a[x][y]);
else
f[i] = min(f[i], dfs(j, flag ^ 1) - b[x][y]);
}
return f[i];
}
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]);
memset(f, -0x3f, sizeof(f));
f[((1 << n) - 1) << m] = 0;
printf("%d", dfs((1 << n) - 1, 1));
return 0;
}

浙公网安备 33010602011771号