P4363 [九省联考2018]一双木棋chess
九省联考D1T1
哇偶,状压DP我果然还是不会的,要多做些才行呢
如果考场考了是不是就TAT了啊
简要题意没有了
上去直接考虑n=2m=2,发现可以手算所有决策
以及还有n=10m=1,会发现我们只能从左到右放
然后没了,正解吧
轮廓线DP
一种挺奇妙的DP状态设计方式啊!
我们把n+m-1个行和列压起来,然后这一位为0表示轮廓线是横着的边,如果为1就表示轮廓线为竖着的边
被轮廓线围住的三角形就是放棋子的格子
然后你会发现这个状态的设计很不错,因为他充分利用了每个信息------这个棋盘一定满足放置是一个从右上到左下的三角形
而且我们可以通过0/1来定位棋子具体在哪一个格子,从而实现转移qwq
eg:

code:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
namespace fastIO {
#define BUF_SIZE (1<<19)
static char buf[BUF_SIZE], *p1 = buf + BUF_SIZE, *pend = buf + BUF_SIZE;
inline char nc() {
if(p1 == pend) {
p1 = buf;
pend = buf + fread(buf, 1, BUF_SIZE, stdin);
}
return *p1++;
}
inline int read() {
int x = 0, f = 1;
register char s = nc();
for(; !isdigit(s); s = nc())if(s == '-')f = -1;
for(; isdigit(s); s = nc())x = (x << 1) + (x << 3) + s - '0';
return x * f;
}
}
using namespace fastIO;
const int MAXN = 10;
const int P = 1e9 + 7;
int a[MAXN][MAXN], b[MAXN][MAXN];
int f[1 << (MAXN << 1)];
int dfs(int sta, bool who, int n, int m) {
if(~f[sta])return f[sta];
f[sta] = who ? -P : P;
int x = n, y = 0;
for(int i = 0; i < n + m - 1; ++i) {
if(sta >> i & 1)x--;
else y++;
if((sta >> i & 3) != 1)
continue;
int nxt = sta ^ (3 << i);
if(who)
f[sta] = max(f[sta], dfs(nxt, who ^ 1, n, m) + a[x][y]);
else
f[sta] = min(f[sta], dfs(nxt, who ^ 1, n, m) - b[x][y]);
}
return f[sta];
}
int main() {
// freopen("test.in", "r", stdin);
int n = read(), m = read();
for(int i = 0; i < n; ++i) {
for(int j = 0; j < m; ++j) {
a[i][j] = read();
}
}
for(int i = 0; i < n; ++i) {
for(int j = 0; j < m; ++j) {
b[i][j] = read();
}
}
memset(f, -1, sizeof(f));
f[((1 << n) - 1) << m] = 0;
printf("%d\n", dfs((1 << n) - 1, 1, n, m));
}