[ABC296Ex] Unite 题解
考虑状压 dp。
设 \(f_{i,j,s}\) 表示当前正在决策坐标为 \((i,j)\) 的格子,且列状态为 \(s\)。其中列状态维护了当前轮廓线上的连通块,我们可以使用最小表示法来简单维护。
(为什么不用广义括号序列?因为其涉及到 \(5\) 个可选值,由于 \(m\le 7\),所以这两个都需要用到八进制,而最小表示法显然要好写很多。)
如果我们选择将当前格子变成黑色,那么相当于合并了当前格上面和左边的两个连通块。
如果我们保持当前格子为白色,那么啥都不变,但要注意,这可能会导致某个连通块被孤立导致错解。
直接统计答案即可。
#include <iostream>
#include <map>
using namespace std;
const int kN = 101, kM = 7;
const int kL = 1 << 3 * kM;
int n, m, o, l, li, ans = 1e9, gc[kL], gt[kL][kM], ct[kL][8];
char a[kN][kM];
map<int, int> f[2];
int G(int s, int i) { return i < 0 ? 0 : (s >> i * 3 & 7); }
int S(int s, int i, int v) { return s ^ (G(s, i) << i * 3) ^ (v << i * 3); }
void U(int i, int v) {
auto p = f[o].find(i);
if (p != f[o].end()) {
p->second = min(p->second, v);
} else {
f[o][i] = v;
}
}
int main() {
ios_base::sync_with_stdio(0), cin.tie(0);
cin >> n >> m;
l = 1 << 3 * m;
for (int i = 0; i < l; ++i) {
for (int j = 0; j < m; ++j) {
++ct[i][G(i, j)];
}
for (int j = 1; j <= m; ++j) {
if (!ct[i][j]) {
gc[i] = j;
break;
}
}
for (int j = 1; j < m; ++j) {
int vl = G(i, j - 1), vu = G(i, j);
gt[i][j] = i;
for (int k = 0; k < m; ++k) {
if (G(gt[i][j], k) == vu) {
gt[i][j] = S(gt[i][j], k, vl);
}
}
}
}
for (int i = 1; i <= n; ++i) {
for (int j = 0; j < m; ++j) {
cin >> a[i][j];
}
}
for (int i = n; i >= 1; --i) {
for (int j = m - 1; j >= 0; --j) {
if (a[i][j] == '#') {
li = i;
break;
}
}
if (li) {
break;
}
}
f[o][0] = 0;
for (int i = 1; i <= li; ++i) {
for (int j = 0; j < m; ++j) {
o ^= 1;
f[o].clear();
for (auto k : f[!o]) {
int s = k.first, fv = k.second;
int vl = G(s, j - 1), vu = G(s, j), v = (a[i][j] == '.');
if (!vl && !vu) { // 新开一个连通块
U(S(s, j, gc[s]), fv + v);
} else if (vl && !vu) { // 接上左边的连通块
U(S(s, j, vl), fv + v);
} else if (!vl && vu) { // 接上上面的连通块
U(s, fv + v);
} else { // 将左边和上面的连通块接起来
U(gt[s][j], fv + v);
}
if (a[i][j] == '.') { // 不涂黑
int vu = G(s, j);
if (!vu || ct[s][vu] > 1) { // 不能孤立某个连通块
U(S(s, j, 0), fv);
}
}
}
}
}
for (auto p : f[o]) {
int i = p.first, fv = p.second;
int g = 0;
for (int j = 0; j < m; ++j) {
int v = G(i, j);
if (!g) {
g = v;
} else if (v && v != g) {
g = -1;
break;
}
}
if (~g) {
ans = min(ans, fv);
}
}
cout << ans;
return 0;
}

浙公网安备 33010602011771号