题面
解法
斯坦纳树的经典应用
- 问题就是给 k k 个关键点,构造一个连通块,使得这个点连通且代价最小,斯坦纳树就可以解决这类问题
- 其实 k k 和棋盘的大小都并没有那么大,只有,所以我们不妨考虑状压dp
- 说白了,斯坦纳树这个名词听起来确实挺nb的,但是冷静一下发现就是一个状压dp
- 可以发现,只有2个关键点的时候一定是走最短路,如果出现超过2个点,就不会是最小生成树的情况,中间可能会选择在一个不是关键点的地方分叉
- 所以,我们考虑这样设计状态: fi,j,s f i , j , s 表示当前分叉点假设为 (i,j) ( i , j ) ,关键点的连通状态为 s s 的最小代价
- 显然,转移只有2种
- 枚举子集,即,减去 ai,j a i , j 是因为它在转移的时候被重复计算了一次
- 当然,也可以是 (i,j) ( i , j ) 连向另外某一个点,然后这个点对关键点的连通状态为 s s ,即
- 但是可以发现,其实我们对于第二种转移并不需要用 O(nm) O ( n m ) 的复杂度枚举那个点在哪里,我们可以通过周围的四个点来迭代出自己的最优解
- 所以,我们对于第二种转移,可以先找到存在解的位置,然后用spfa更新一下即可,这样就省去了每一次转移 O(nm) O ( n m ) 的复杂度,可以直接在spfa中求出来
- 因为题目中需要求出具体的方案,所以考虑在转移的时候记录一下这个状态是由之前的哪一个状态转移到的即可,然后我们就可以发现第二种转移如果要枚举点的话对于方案的输出并不是那么好处理
- 时间复杂度: O(nm×(3k+nm)) O ( n m × ( 3 k + n m ) )
【注意事项】
- 建议处理好边界条件,否则可能会挂,
只针对我这种蒟蒻 - 枚举子集的时候注意一下,我一开始就是因为枚举子集没有枚举全部的子集
(就是写错了)然后就WA光了
代码
#include <bits/stdc++.h>
#define N 11
using namespace std;
template <typename node> void chkmax(node &x, node y) {x = max(x, y);}
template <typename node> void chkmin(node &x, node y) {x = min(x, y);}
template <typename node> void read(node &x) {
x = 0; int f = 1; char c = getchar();
while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
struct Node {
int x, y;
} b[N];
struct Info {
int x, y, s;
} pre[N][N][1 << N];
int dx[5] = {0, -1, 1, 0, 0}, dy[5] = {0, 0, 0, -1, 1};
int n, m, a[N][N], vis[N][N], used[N * N], f[N][N][1 << N];
queue <Node> q;
int lowbit(int x) {return x & -x;}
int calc(int x, int y) {return (x - 1) * m + y;}
void spfa(int s) {
while (!q.empty()) {
Node tmp = q.front(); q.pop();
int x = tmp.x, y = tmp.y; used[calc(x, y)] = 0;
for (int i = 1; i <= 4; i++) {
int tx = x + dx[i], ty = y + dy[i];
if (tx && ty && tx <= n && ty <= m && f[tx][ty][s] > f[x][y][s] + a[tx][ty]) {
f[tx][ty][s] = f[x][y][s] + a[tx][ty];
pre[tx][ty][s] = (Info) {x, y, s};
if (!used[calc(tx, ty)]) used[calc(tx, ty)] = 1, q.push((Node) {tx, ty});
}
}
}
}
void dfs(int x, int y, int s) {
if (!s) return;
Info tmp = pre[x][y][s]; vis[x][y] = 1;
dfs(tmp.x, tmp.y, tmp.s);
if (tmp.x == x && tmp.y == y) dfs(tmp.x, tmp.y, s - tmp.s);
}
int main() {
read(n), read(m); int t = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
for (int s = 0; s < (1 << 10); s++)
f[i][j][s] = INT_MAX;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) {
read(a[i][j]), f[i][j][0] = a[i][j];
if (!a[i][j]) b[++t] = (Node) {i, j};
}
for (int i = 1; i <= t; i++) {
f[b[i].x][b[i].y][1 << i - 1] = 0;
q.push(b[i]); spfa(1 << i - 1);
}
for (int s = 1; s < (1 << t); s++) {
if (s == lowbit(s)) continue;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) {
for (int k = s & (s - 1); k; k = s & (k - 1))
if (f[i][j][s] > f[i][j][s - k] + f[i][j][k] - a[i][j]) {
f[i][j][s] = f[i][j][k] + f[i][j][s - k] - a[i][j];
pre[i][j][s] = (Info) {i, j, k};
}
if (f[i][j][s] != INT_MAX) q.push((Node) {i, j}), used[calc(i, j)] = 1;
}
spfa(s);
}
int ans = INT_MAX, x, y;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
if (f[i][j][(1 << t) - 1] < ans)
ans = f[i][j][(1 << t) - 1], x = i, y = j;
cout << ans << "\n"; dfs(x, y, (1 << t) - 1);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++)
if (!a[i][j]) cout << 'x';
else if (vis[i][j]) cout << 'o';
else cout << '_';
cout << "\n";
}
return 0;
}