一雙木棋 chess
阿新 • • 發佈:2022-03-01
題意
\(n \times m, (n, m\leq 10)\) 的矩陣,每次可以在左上都有棋子的地方下,奇數次下在 \((i, j)\) 的價值是 \(a_{i, j}\),偶數次是 \(-b_{i, j}\)。
奇數次時想讓價值和最大,偶數次時想讓價值和最小。
問最終結果。
輪廓線dp,狀壓dp
狀態
如果第 \(i\) 行放了 \(c_i\) 個,那麼它一定左到右連續的。
並且從第 \(1\) 行到第 \(n\) 行,\(c_i\) 是遞減的。
狀態就可以用輪廓線唯一表示。
用 \(0\) 表示 \(m\) 條橫向的線,用 \(1\) 表示 \(n\) 條豎向的線。
令 \(f[S]\)
初始和最終狀態
由於轉移是從最終倒回來的。
初始狀態 \(f[111\cdots 0000\cdots] = 0\), 表示全部下完離全部下完的價值和是 0。
其它全部設為無窮大或無窮小。
最終狀態 \(f[0000\cdots 111\cdots]\) 表示初始狀態離全部下完的價值。
轉移
能發現選一格,實際上的輪廓線就是把 \(1\) 和 下一位的 \(0\) 交換。
選的位置可以用輪廓線狀態算出來,設為 \((x, y)\)。
所以就有:
1.偶數次時
2.奇數次時
分析
狀態是 \(O(2^{n + m})\) 的,轉移是 \(O(n + m)\)。
最終是 \(O(2^{n + m}(n + m))\)
程式碼
#include<bits/stdc++.h> using namespace std; using ll = long long; const int MAXN = 15; const int INF = 0x3f3f3f3f; //const int mod = 1000000007; int mod; const double eps = 1e-9; template <typename T> void Read(T &x) { x = 0; T f = 1; char a = getchar(); for(; a < '0' || '9' < a; a = getchar()) if (a == '-') f = -f; for(; '0' <= a && a <= '9'; a = getchar()) x = (x * 10) + (a ^ 48); x *= f; } inline int add(const int &a, const int &b) { static int c; c = a + b; if (c >= mod) c -= mod; if (c < 0) c += mod; return c; } inline int mul(const int &a, const int &b) { return 1ll * a * b % mod; } int qpow(int a, int b) { int sum(1); while(b) { if (b & 1) sum = mul(sum, a); a = mul(a, a); b >>= 1; } return sum; } int n, m; int a[MAXN][MAXN], b[MAXN][MAXN]; int f[1 << 21]; bool vis[1 << 21]; int dfs(int S, int N) { if (vis[S]) return f[S]; vis[S] = 1; f[S] = N ? - INF : INF; int x = n, y = 0; for (int i = 0; i < n + m - 1; i ++) { if (S >> i & 1) x --; else y ++; if ((S >> i & 3) != 1) continue; int to = S ^ (3 << i); if (N) f[S] = max(f[S], dfs(to, N ^ 1) + a[x + 1][y + 1]); else f[S] = min(f[S], dfs(to, N ^ 1) - b[x + 1][y + 1]); } return f[S]; } int main() { 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(f, 0x3f, sizeof(f)); f[((1 << n) - 1) << m] = 0; vis[((1 << n) - 1) << m] = 1; cout << dfs((1 << n) - 1, 1); return 0; }