【树】斯坦纳树

斯坦纳树

学习资料:OI Wiki

问题:

在一个无向带权连通图 (G=(V,E)) 中,对点集 (Ssubset V) 求一边集 (Csubset E) ,使得 (uin S) 相互连通且 (sum w_c(cin C)) 的值最小。

也就是,对一个无向图 (G),求一个对于点集 (S) 的最小生成树。

做法:

状压+dp+最短路

时间复杂度:

(mathcal{O(n imes3^k+mlogm imes2^k)})(|S|=k)

模板题:

LuoguP6192 最小斯坦纳树

代码:

#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define mkp(a,b) make_pair(a,b)
using namespace std;
typedef long long ll;
const int maxn=5e5+5;
const int inf=0x3f3f3f3f;

int head[maxn],to[maxn<<1],nxt[maxn<<1],ww[maxn<<1],pcnt;
inline void add(int u,int v,int w){
    to[++pcnt]=v;nxt[pcnt]=head[u];ww[pcnt]=w;head[u]=pcnt;
}
int a[maxn];
int dp[111][2222];
priority_queue<pair<int,int>>que;
void Dij(int sta)
{
    pair<int,int>pr;
    while(!que.empty())
    {
        pr=que.top();que.pop();
        for(int i=head[pr.second];i;i=nxt[i]){
            if(dp[to[i]][sta]>dp[pr.second][sta]+ww[i]){
                dp[to[i]][sta]=dp[pr.second][sta]+ww[i];
                que.push(mkp(-dp[to[i]][sta],to[i]));
            }
        }
    }
}
int main()
{
    int n,m,k,u,v,w;
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w);add(v,u,w);
    }
    for(int i=1;i<=k;i++)scanf("%d",&a[i]);
    mem(dp,inf);
    for(int i=1;i<=k;i++)dp[a[i]][1<<i-1]=0;
    for(int sta=1,up=1<<k;sta<up;sta++){
        for(int i=1;i<=n;i++)
        {
            for(int s=sta&(sta-1);s;s=sta&(s-1))
                dp[i][sta]=min(dp[i][sta],dp[i][sta^s]+dp[i][s]);
            if(dp[i][sta]!=inf)que.push(mkp(-dp[i][sta],i));
        }
        Dij(sta);
    }
    printf("%d
",dp[a[1]][(1<<k)-1]);
}

原文地址:https://www.cnblogs.com/kkkek/p/13676048.html