[题解][洛谷P6192]【模板】最小斯坦纳树

斯坦纳树是一棵连接给定点集的树,其中边不一定在这些点里面连。
可以使用 dp,设 \(f_{i,S}\) 表示 钦定 \(i\) 为根,连接了集合 \(S\) 的最小代价,转移分两种情况:
\(i\) 度数为 \(1\),可以转移到和它相连的点 \(j\)
\(i\) 度数不为 \(1\),可以由子集转移。
第一种转移用最短路求即可。

const int N=110,M=510;
int n,m,k,ss[N];
struct Edge {
  int to,nxt,w;
}e[M<<1];
int hd[N],cnt;
il void ade(int u,int v,int w){
  e[++cnt].to=v,e[cnt].w=w,e[cnt].nxt=hd[u],hd[u]=cnt;
}
int f[N][2000];bool vis[N];
queue<int> q;
void SPFA(int S){
  memset(vis,0,sizeof vis);
  for(rg int i=1;i<=n;i++){
    if(f[i][S]!=INF)q.push(i),vis[i]=1;
  }
  while(!q.empty()){
    int u=q.front();q.pop(),vis[u]=0;
    for(rg int i=hd[u];i;i=e[i].nxt){
      int v=e[i].to;
      if(f[v][S]>f[u][S]+e[i].w){
        f[v][S]=f[u][S]+e[i].w;
        if(!vis[v])q.push(v),vis[v]=1;
      }
    }
  }
}
int main(){
  Read(n),Read(m),Read(k);
  for(rg int i=1,u,v,w;i<=m;i++){
    Read(u),Read(v),Read(w),ade(u,v,w),ade(v,u,w);
  }
  memset(f,0x3f,sizeof f);
  for(rg int i=1;i<=k;i++){
    Read(ss[i]);
    f[ss[i]][1<<i-1]=0;
  }
  for(rg int S=0;S<(1<<k);S++){
    for(rg int i=1;i<=n;i++){
      for(rg 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);
  }
  cout<<f[ss[1]][(1<<k)-1]<<endl;
  KafuuChino HotoKokoa
}
posted @ 2021-10-11 10:26  ajthreac  阅读(56)  评论(0编辑  收藏  举报