最小斯坦納樹
阿新 • • 發佈:2021-09-17
題意:
給出一張 \(n\) 個點的無向圖,其中 \(k\) 個點是關鍵點,問可以將所有關鍵點相連的最小網路是多大。
Solution:
考慮 DP ,\(f_{i,S}\) 表示做到 \(i\) 號點,包含關鍵點的集合為 \(S\) ,考慮轉移有兩種方式:
- \(f_{i,S|T} = f_{i,S} + f_{i,T}\)
- \(f_{x,S} = f_{y,S} + w(y,x)\)
第一個列舉子集,第二個 spfa 或 dij 轉移。
#include<bits/stdc++.h> #define fo(i,a,b) for(int i=a;i<=b;++i) #define fd(i,a,b) for(int i=a;i>=b;--i) using namespace std; const int N=110; const int M=1010; const int SZ=1e5; const int LIM=1030; const int INF=1e9+7; int n,m,k,tot,last[N],f[N][LIM],ans; bool bz[N]; int st,en; int q[SZ+5]; struct edge{ int st,en,v,next; }E[M]; void add(int x,int y,int z){ E[++tot]=(edge){x,y,z,last[x]}; last[x]=tot; } void spfa(int S){ st=en=0; fo(i,1,n) if(f[i][S]<f[0][0])q[++en]=i,bz[i]=1; while(st!=en){ int x=q[++st]; for(int p=last[x];p;p=E[p].next){ int y=E[p].en; if(f[y][S]>f[x][S] + E[p].v){ f[y][S]=f[x][S] + E[p].v; if(!bz[y]){ q[++en]=y; bz[y]=1; } } } bz[x]=0; } } int main(){ freopen("data.in","r",stdin); freopen("data.out","w",stdout); scanf("%d%d%d",&n,&m,&k); fo(i,1,m){ int x,y,z; scanf("%d%d%d",&x,&y,&z); add(x,y,z);add(y,x,z); } memset(f,127,sizeof(f)); fo(i,1,k){ int x; scanf("%d",&x); f[x][1<<i-1]=0; } fo(i,1,n)f[i][0]=0; int lim=(1<<k)-1; fo(S,1,lim){ fo(i,1,n){ for(int T=S&(S-1);T;T=S&(T-1)){ f[i][S]=min(f[i][S] ,f[i][T] + f[i][S^T]); } } spfa(S); } ans=INF; fo(i,1,n)ans=min(ans ,f[i][lim]); printf("%d\n",ans); return 0; }