[模板] 最小斯坦纳树
[模板] 最小斯坦纳树
也不知道干什么的
引入
给定一个无向图 \(G=(V,E)\) 以及一个包含 \(k\) 个点的点集 \(S\),要求构造一个图 \(G'\),使得:
- \(S\sub V'\);
- \(G'\) 为连通图;
- \(E'\) 中所有边的边权和最小。
\(n\leq 100,m\leq 500,k\leq 10\)
最小化图的边权和。
解法
因为 \(k\) 比较小,显然是可以状压的。
可以考虑将原图中的点不断合并的过程,并转化为有根树来求解。
设计状态为 \(f_{S,i}\) 表示当前生成的图中包含了 \(S\) 中的关键点,当前扩展到了 \(i\) 号结点的最小边权和。
考虑两种转移:
- 拓展根操作
\[f_{S,i}->f_{S,j}
\]
对应到图上就是向上拓展了一个单位,显然贪心的来选是需要最短路径的,因此一边 \(dijstra\) 一边扩展。
- 合并操作
\[f_{S1,i}+f_{S2,i}->f_{S,i}\ (S=S1+S2)
\]
可以通过枚举子集来实现。
注意:需要先进行合并,再在合并后的基础上进行扩展操作。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#include <queue>
const int maxn = 100 + 10 , maxm = 500 + 10;
int head[maxn],cnt=0;
struct edge{
int to,nxt,w;
}e[maxm<<1];
inline void link(int u,int v,int w){
e[++cnt].to=v;e[cnt].nxt=head[u];head[u]=cnt;e[cnt].w=w;
}
int f[(1<<10)+5][maxn];
int n,m,k,p[maxn];
bool vis[maxn];
#define Pair pair<int,int>
#define mp make_pair
priority_queue<Pair,vector<Pair>,greater<Pair> > q;
void dij(int s){
memset(vis,false,sizeof vis);
while(q.size()){
int u=q.top().second;q.pop();
if(vis[u])continue;
vis[u]=true;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(!vis[v] && f[s][v]>f[s][u]+e[i].w){
f[s][v]=f[s][u]+e[i].w;
q.push(mp(f[s][v],v));
}
}
}
}
#define read() read<int>()
int main(){
n=read();m=read();k=read();
for(int i=1;i<=m;i++){
int u=read(),v=read(),w=read();link(u,v,w);link(v,u,w);
}
memset(f,0x3f,sizeof f);
for(int i=1;i<=k;i++){
p[i]=read();
f[1<<i-1][p[i]]=0;
}
for(int s=0;s<(1<<k);s++){
for(int i=1;i<=n;i++){
for(int s0=(s-1)&s;s0;s0=(s0-1)&s){
f[s][i]=min(f[s][i],f[s0][i]+f[s^s0][i]);
}
if(f[s][i]!=0x3f3f3f3f)q.push(mp(f[s][i],i));
}
dij(s);
}
printf("%d\n",f[(1<<k)-1][p[1]]);
return 0;
}