【题目链接】
https://www.lydsy.com/JudgeOnline/problem.php?id=2595
【题解】
斯坦纳树模板题。
记
表示当前最小生成树的根在
,连在这棵树上的关键点(景点)的状态为
,(k是一个二进制数,表示关键点是否与根连通)。
转移分为当前层转移和跨层转移,当前层转移就是在
不变的状态下用spfa更新答案。跨层转移就是在
不变的情况下用枚举子集的方式更新答案。
具体来说就是:
1)
2)
为了输出方案,每个状态再记录通过哪个方式的哪个状态转移过来,回溯即可。
时间复杂度
【代码】
/* - - - - - - - - - - - - - - -
User : VanishD
problem : [bzoj2595]
Points : Steiner Tree
- - - - - - - - - - - - - - - */
# include <bits/stdc++.h>
# define ll long long
# define inf 0x3f3f3f3f
# define N 11
# define M 110
# define K 10
using namespace std;
int read(){
int tmp = 0, fh = 1; char ch = getchar();
while (ch < '0' || ch > '9'){ if (ch == '-') fh = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9'){ tmp = tmp * 10 + ch - '0'; ch = getchar(); }
return tmp * fh;
}
int use[N][N], qx[M], qy[M], f[N][N][1 << K], tag[N][N][1 << K], frmx[N][N][1 << K], frmy[N][N][1 << K], n, m, mp[N][N], cntx[N], cnty[N], id, to[N][N];
const int dx[] = {1, 0, -1, 0}, dy[] = {0, 1, 0, -1};
void spfa(int p){
int pl = 1, pr = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++){
use[i][j] = true;
qx[++pr] = i; qy[pr] = j;
}
while (pl <= pr){
int nowx = qx[pl % M], nowy = qy[(pl++) % M];
for (int i = 0; i < 4; i++){
int tmpx = nowx + dx[i], tmpy = nowy + dy[i];
if (tmpx < 1 || tmpx > n || tmpy < 1 || tmpy > m) continue;
if (f[tmpx][tmpy][p] > f[nowx][nowy][p] + mp[tmpx][tmpy]){
f[tmpx][tmpy][p] = f[nowx][nowy][p] + mp[tmpx][tmpy];
tag[tmpx][tmpy][p] = 1, frmx[tmpx][tmpy][p] = nowx, frmy[tmpx][tmpy][p] = nowy;
if (use[tmpx][tmpy] == false){
use[tmpx][tmpy] = true;
qx[(++pr) % M] = tmpx; qy[pr % M] = tmpy;
}
}
}
use[nowx][nowy] = false;
}
}
void solve(int x, int y, int tmp){
if (tag[x][y][tmp] == -1) return;
if (tag[x][y][tmp] == 1){
to[x][y] = true;
solve(frmx[x][y][tmp], frmy[x][y][tmp], tmp);
}
else {
solve(x, y, frmx[x][y][tmp]);
solve(x, y, frmy[x][y][tmp]);
}
}
int main(){
// freopen("bzoj2595.in", "r", stdin);
// freopen("bzoj2595.out", "w", stdout);
n = read(), m = read();
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++){
mp[i][j] = read();
if (mp[i][j] == 0){
cntx[id] = i, cnty[id] = j;
id++;
}
}
memset(f, inf, sizeof(f));
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++){
f[i][j][0] = 0;
tag[i][j][0] = -1;
}
for (int i = 0; i < id; i++){
f[cntx[i]][cnty[i]][1 << i] = 0;
tag[cntx[i]][cnty[i]][1 << i] = -1;
}
for (int i = 0; i < (1 << id); i++){
for (int j = 1; j <= n; j++)
for (int k = 1; k <= m; k++)
for (int t = i; t > 0; t = (t - 1) & i){
if (f[j][k][i] > f[j][k][t] + f[j][k][i - t] - mp[j][k]){
f[j][k][i] = f[j][k][t] + f[j][k][i - t] - mp[j][k];
tag[j][k][i] = 0;
frmx[j][k][i] = t; frmy[j][k][i] = i - t;
}
}
spfa(i);
}
int ans = inf, ansx, ansy;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++){
int tmp = f[i][j][(1 << id) - 1];
if (ans > tmp){
ansx = i, ansy = j;
ans = tmp;
}
}
printf("%d\n", ans);
solve(ansx, ansy, (1 << id) - 1);
for (int i = 1; i <= n; i++){
for (int j = 1; j <= m; j++)
if (mp[i][j] == 0) printf("x");
else if (to[i][j] == true) printf("o");
else printf("_");
printf("\n");
}
return 0;
}