Codeforces 903F Clear The Matrix(状态压缩DP)
题目链接 Clear The Matrix
题意 给定一个$4 * n$的矩形,里面的元素为$'.'$或$'*'$。现在有$4$种正方形可以覆盖掉$'*'$,正方形的边长分别为$1,2,3,4$。
求把整个矩形变成全$'.'$的最小代价。
考虑状压DP
设$f[i][j]$为前$i$列已经全部变成'.',第$i + 1$到第$i + 4$列的这$16$个格子状态为$j$的最小花费。
这$16$个格子标号如下
$0$ $4$ $8$ $12$
$1$ $5$ $9$ $13$
$2$ $6$ $10$ $14$
$3$ $7$ $11$ $15$
我们可以枚举$0,1,2,3$这$4$个格子。以当前格子为左上角的正方形的边长。
其中$0$号格子可以放边长为$0, 1, 2, 3, 4$的正方形;
$1$号格子可以放边长为$0, 1, 2, 3$的正方形;
$2$号格子可以放边长为$0, 1, 2$的正方形;
$3$号格子可以放边长为$0, 1$的正方形;
放边长为$0$的正方形等效为不放。
当枚举的这些正方形可以完全盖住$0,1,2,3$这$4$个格子的时候,就可以进行状态转移。
状态稍微有点复杂,用二进制位表示……
时间复杂度$O(n * 2^{16} * 5!)$
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) const int N = 1e3 + 10; const int S = 1 << 16; char s[N]; int f[N][S + 2]; int a[6][N]; int c[10]; int g[10]; int n; int pre[N]; int ans; int cnt, mask; void up(int &a, int b){ if (a > b) a = b;} inline get(int x){ return x ^ (S - 1);} int main(){ scanf("%d", &n); rep(i, 1, 4) scanf("%d", c + i); rep(i, 1, 4){ scanf("%s", s + 1); rep(j, 1, n) a[i][j] = s[j] == '*'; } rep(k, 0, n){ rep(i, 0, S + 1) f[k][i] = 1e9; } cnt = -1; mask = 0; rep(i, 1, 4){ rep(j, 1, 4){ ++cnt; if (a[j][i]) mask |= (1 << cnt); } } f[0][mask] = 0; g[0] = 0; g[1] = 1; g[2] = (1 << 0) ^ (1 << 1) ^ (1 << 4) ^ (1 << 5); g[3] = (1 << 0) ^ (1 << 1) ^ (1 << 2); g[3] ^= ((1 << 4) ^ (1 << 5) ^ (1 << 6)); g[3] ^= ((1 << 8) ^ (1 << 9) ^ (1 << 10)); g[4] = (1 << 16) - 1; rep(k, 0, n){ int extra = 0; rep(j, 1, 4) if (a[j][k + 5]) extra |= (1 << (j + 11)); rep(j, 0, S - 1){ if (f[k][j] >= 1e9) continue; rep(aa, 0, 4){ rep(bb, 0, 3){ rep(cc, 0, 2){ rep(dd, 0, 1){ int cnt = get(g[aa]) & get(g[bb] << 1) & get(g[cc] << 2) & get(g[dd] << 3); if ((cnt & j & 15) == 0){ int nowmask = cnt & j; nowmask >>= 4; nowmask ^= extra; up(f[k + 1][nowmask], f[k][j] + c[aa] + c[bb] + c[cc] + c[dd]); } } } } } } } ans = 1e9; rep(i, n - 4, n) ans = min(ans, f[i][0]); printf("%d\n", ans); return 0; }
我们可以考虑使用滚动数组,于是空间大大节省
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) #define MP make_pair #define fi first #define se second typedef long long LL; const int N = 1e3 + 10; const int S = 1 << 16; char s[N]; int f[2][S + 2]; int a[6][N]; int c[10]; int g[10]; int n; int pre; int ans; int cnt, mask; void up(int &a, int b){ if (a > b) a = b;} inline get(int x){ return x ^ (S - 1);} int main(){ scanf("%d", &n); rep(i, 1, 4) scanf("%d", c + i); rep(i, 1, 4){ scanf("%s", s + 1); rep(j, 1, n) a[i][j] = s[j] == '*'; } rep(k, 0, 1) rep(i, 0, S + 1) f[k][i] = 1e9; cnt = -1; mask = 0; rep(i, 1, 4){ rep(j, 1, 4){ ++cnt; if (a[j][i]) mask |= (1 << cnt); } } f[0][mask] = 0; pre = 0; g[0] = 0; g[1] = 1; g[2] = (1 << 0) ^ (1 << 1) ^ (1 << 4) ^ (1 << 5); g[3] = (1 << 0) ^ (1 << 1) ^ (1 << 2); g[3] ^= ((1 << 4) ^ (1 << 5) ^ (1 << 6)); g[3] ^= ((1 << 8) ^ (1 << 9) ^ (1 << 10)); g[4] = (1 << 16) - 1; rep(i, 0, n){ int extra = 0; rep(j, 1, 4) if (a[j][i + 5]) extra |= (1 << (j + 11)); rep(j, 0, S + 1) f[pre ^ 1][j] = 1e9; rep(j, 0, S - 1){ if (f[pre][j] >= 1e9) continue; rep(aa, 0, 4){ rep(bb, 0, 3){ rep(cc, 0, 2){ rep(dd, 0, 1){ int cnt = get(g[aa]) & get(g[bb] << 1) & get(g[cc] << 2) & get(g[dd] << 3); if ((cnt & j & 15) == 0){ int nowmask = cnt & j; nowmask >>= 4; nowmask ^= extra; up(f[pre ^ 1][nowmask], f[pre][j] + c[aa] + c[bb] + c[cc] + c[dd]); } } } } } } pre ^= 1; } printf("%d\n", f[pre][0]); return 0; }
不过这个做法还不是最优的= =
官方题解给出的做法是只存后面12个格子的状态的
因为当考虑某一列的时候一旦用到$4*4$的正方形,其他边长的正方形就不用再考虑了……直接无视掉。
这样的话可以直接从$f[k][nowmask]$转移到$f[k + 1][0]$
时间复杂度$O(n * 2^{12} * 96)$
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) const int N = 1e3 + 10; const int S = 1 << 12; char s[N]; int f[2][S + 2], a[6][N], c[10], g[10]; int n, x, cnt, mask, ans; void up(int &a, int b){ if (a > b) a = b;} inline get(int x){ return x ^ (S - 1);} int main(){ scanf("%d", &n); rep(i, 1, 4) scanf("%d", c + i); rep(i, 1, 4){ scanf("%s", s + 1); rep(j, 1, n) a[i][j] = s[j] == '*'; } rep(k, 0, 1) rep(i, 0, S + 1) f[k][i] = 1e9; cnt = -1; mask = 0; rep(i, 1, 3){ rep(j, 1, 4){ ++cnt; if (a[j][i]) mask |= (1 << cnt); }} f[0][mask] = 0; x = 0; g[0] = 0; g[1] = 1; g[2] = 51; g[3] = 1911; rep(i, 0, n){ int extra = 0; rep(j, 1, 4) if (a[j][i + 4]) extra |= (1 << (j + 7)); rep(j, 0, S + 1) f[x ^ 1][j] = 1e9; rep(j, 0, S - 1){ if (f[x][j] >= 1e9) continue; rep(aa, 0, 3){ rep(bb, 0, 3){ rep(cc, 0, 2){ rep(dd, 0, 1){ int cnt = get(g[aa]) & get(g[bb] << 1) & get(g[cc] << 2) & get(g[dd] << 3); if ((cnt & j & 15) == 0){ mask = (cnt & j) >> 4; mask ^= extra; up(f[x ^ 1][mask], f[x][j] + c[aa] + c[bb] + c[cc] + c[dd]); } } } } } up(f[x ^ 1][0], f[x][j] + c[4]); } x ^= 1; } printf("%d\n", f[x][0]); return 0; }