题意:洛谷P4294
给定一个矩阵,问一条最小权值路径能够走遍所有景点,并输出最小权值与路径。
分析:
斯坦纳树是什么?(最小生成树是斯坦塔树的一种特殊情况)
按照我的理解,斯坦纳树通常与状压
d
p
dp
dp和最短路联系在一起。
斯坦纳树中的状压
d
p
dp
dp分为两部分:
首先定义状态:
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示选中
i
i
i点为根必须点的状态为
j
j
j的最小权值,
特别地,
d
p
[
i
]
[
1
<
<
j
]
=
0
dp[i][1<<j]=0
dp[i][1<<j]=0。
那么有
1.
1.
1.自己由自己的子集转移而来
d
p
[
i
]
[
j
]
=
m
i
n
(
d
p
[
i
]
[
j
]
,
d
p
[
i
]
[
s
]
+
d
p
[
i
]
[
k
]
)
dp[i][j]=min(dp[i][j],dp[i][s]+dp[i][k])
dp[i][j]=min(dp[i][j],dp[i][s]+dp[i][k]),
s
s
s
⊆
\subseteq
⊆
j
j
j,
k
k
k
∁
\complement
∁
s
s
s
s
s
s是
j
j
j的真子集,
k
k
k是
s
s
s的补集。
2.
2.
2.由同一集合不同路径转移而来
d
p
[
i
]
[
j
]
=
m
i
n
(
d
p
[
i
]
[
j
]
,
d
p
[
k
]
[
j
]
+
e
d
g
e
[
k
dp[i][j]=min(dp[i][j],dp[k][j]+edge[k
dp[i][j]=min(dp[i][j],dp[k][j]+edge[k
→
\rightarrow
→
i
]
.
v
a
l
)
i].val)
i].val)
这个方程的意思是
i
i
i点有可能是由
i
i
i点相邻的点转移而来的,观察这个方程可以想到最短路松弛操作。故上
d
i
j
k
dijk
dijk或
s
p
f
a
spfa
spfa。
由于本题不是边权而是点权,所以方程要做一些微小的变化:
1.
1.
1.
d
p
[
i
]
[
j
]
=
m
i
n
(
d
p
[
i
]
[
j
]
,
d
p
[
i
]
[
s
]
+
d
p
[
i
]
[
k
]
−
p
i
c
[
i
]
[
j
]
)
dp[i][j]=min(dp[i][j],dp[i][s]+dp[i][k]-pic[i][j])
dp[i][j]=min(dp[i][j],dp[i][s]+dp[i][k]−pic[i][j]),
s
s
s
⊆
\subseteq
⊆
j
j
j,
k
k
k
∁
\complement
∁
s
s
s
2.
2.
2.
d
p
[
i
]
[
j
]
=
m
i
n
(
d
p
[
i
]
[
j
]
,
d
p
[
k
]
[
j
]
+
p
i
c
[
i
]
[
j
]
)
dp[i][j]=min(dp[i][j],dp[k][j]+pic[i][j])
dp[i][j]=min(dp[i][j],dp[k][j]+pic[i][j])
接下来就是输出路径的问题了,我们用 p a i r < p a i r < i n t , i n t > , i n t > pair<pair<int,int>,int> pair<pair<int,int>,int>记录转移时是从哪转移的即可, p a i r pair pair里的 p a i r pair pair记录点坐标,另外一个记录状态。最后从一个选定的景点 d f s dfs dfs即可。 d f s dfs dfs时注意有自己向自己转移的情况,此时 d f s dfs dfs子集和子集的补集即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define pii pair<int, int>
#define ppi pair<pii, int>
#define mp make_pair
#define fi first
#define se second
const int maxn = 10 + 5;
const int maxm = (1 << 10) + 5;
const int inf = 0x3f3f3f3f;
int n, m, k, x, y, v, from, num, maxsta;
int dp[maxn * maxn][maxm], pic[maxn][maxn], bel[maxn][maxn];
int to[4][2] = {1, 0, -1, 0, 0, 1, 0, -1};
bool vis[maxn][maxn], use[maxn][maxn];
pii rt;
ppi pre[maxn * maxn][maxm];
queue<pii> q;
bool check(int x, int y){
if(x < 1 || y < 1 || x > n || y > m) return false;
return true;
}
void spfa(int sta){
while(!q.empty()){
pii u = q.front();
q.pop();
vis[u.fi][u.se] = 0;
from = bel[u.fi][u.se];
for(int i = 0; i < 4; i++){
x = u.fi + to[i][0];
y = u.se + to[i][1];
v = bel[x][y];
if(check(x, y)){
if(dp[v][sta] > dp[from][sta] + pic[x][y]){
dp[v][sta] = dp[from][sta] + pic[x][y];
pre[v][sta] = mp(mp(u.fi, u.se), sta);
if(!vis[x][y]) q.push(mp(x, y)), vis[x][y] = 1;
}
}
}
}
}
void dfs(pii rt, int sta){
int u = bel[rt.fi][rt.se];
if(!pre[u][sta].se) return ;
use[rt.fi][rt.se] = true;
if(pre[u][sta].fi == rt) dfs(rt, pre[u][sta].se ^ sta);
dfs(pre[u][sta].fi, pre[u][sta].se);
}
int main(){
scanf("%d %d", &n, &m);
memset(dp, inf, sizeof dp);
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
scanf("%d", &pic[i][j]);
bel[i][j] = ++num;
if(!pic[i][j]) dp[num][1 << (k++)] = 0, rt = mp(i, j);
}
}
maxsta = 1 << k;
for(int sta = 0; sta < maxsta; sta++){
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
int u = bel[i][j];
for(int s = sta; s; s = (s - 1) & sta){
if(dp[u][sta] > dp[u][s] + dp[u][sta ^ s] - pic[i][j]){
dp[u][sta] = dp[u][s] + dp[u][sta ^ s] - pic[i][j];
pre[u][sta] = mp(mp(i, j), s);
}
}
if(dp[u][sta] < inf) q.push(mp(i, j)), vis[i][j] = 1;
}
}
spfa(sta);
}
printf("%d\n", dp[bel[rt.fi][rt.se]][(1 << k) - 1]);
dfs(rt, (1 << k) - 1);
for(int i = 1; i <= n; i++, putchar('\n'))
for(int j = 1; j <= m; j++)
if(!pic[i][j]) putchar('x');
else if(use[i][j]) putchar('o');
else putchar('_');
return 0;
}