给出n个点,然后给出m条双向边,边有边权。
再给出一个大小为k的点集,求使得点集联通的最小花费。
时间复杂度O(n*3^k + cE*2^k),其中c为spfa常数。
#include <bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 1050;
const int M = 1e6;
const int INF = 0x3f3f3f3f;
int n,m,k,dp[N][1<<10],tot,endSt,head[N];
bool vis[N][1<<10];
queue<int>q;
struct E{
int v,w,nxt;
}edge[M];
void ae(int u,int v,int w) {
edge[++tot] = (E){v,w,head[u]};
head[u] = tot;
edge[++tot] = (E){u,w,head[v]};
head[v] = tot;
}
void spfa(int sta) {
while(!q.empty()) {
int u = q.front();
q.pop();
vis[u][sta] = 0;
for(int i = head[u]; ~i; i = edge[i].nxt) {
int v = edge[i].v;
int w = edge[i].w;
if(dp[v][sta]>dp[u][sta]+w) {
dp[v][sta] = dp[u][sta]+w;
if(vis[v][sta]) continue;
vis[v][sta] = 1;
q.push(v);
}
}
}
}
void init() {
tot = 0;
memset(dp,0x3f,sizeof(dp));
memset(head,-1,sizeof(head));
memset(vis,0,sizeof(vis));
}
int main() {
ios::sync_with_stdio(0);
while(cin>>n>>m) {
init();
rep(i, 1, m) {
int u,v,w;
cin>>u>>v>>w;
ae(u,v,w);
}
cin>>k;
int sup; //为任意一个关键点
rep(i, 1, k) {
int x;
cin>>x;
sup = x;
dp[x][1<<(i-1)] = 0;
}
endSt = 1<<k;
rep(sta, 0, endSt-1) {
rep(i, 1, n) {
for(int sub = (sta-1)&sta; sub; sub = (sub-1)&sta)
dp[i][sta] = min(dp[i][sta],dp[i][sub]+dp[i][sta-sub]);
if(dp[i][sta]!=INF) q.push(i),vis[i][sta] = 1;
}
spfa(sta);
}
cout<<dp[sup][endSt-1]<<endl;
}
return 0;
}