斯坦纳树

斯坦纳树问题大概是这样一个问题:给定平面上 (n) 个点,然后求把这些点连起来的最短路径是多长,点有点权。

如果放在 OI 上,那么就可以变成这样一个问题,给定一张联通的无向图,给定 (k) 个关键点,问把这 (k) 个关键点联通的边权和最小是多少,边有边权。

解决方法

我们设计一个状压 dp,(f_{i,s}) 表示目前包含关键点的集合为 (k),目前这颗树的根为 (i),有两种转移:

  • (f_{i,s}:=min(f_{i,s},f_{i,sub}+f_{i,overline{sub}}))
  • (f_{i,s}:=min(f_{i,s},f_{j,s}+w(i,j)))

当然后者得保证 (i,j) 联通。

第一个不难转移,我们首先枚举 (s),在枚举根,然后枚举子集转移即可。

第二个转移很像最短路,我们不妨以状态作为其实点跑迪杰斯特拉来做最短路,我们采用优先队列,用优的去更新劣的,正确性显然。

这个代码只是一个最初始的代码,有一些毒瘤题目需要卡常才能过。

例题

模板题

代码:

#include<bits/stdc++.h>
#define dd double
#define ld long double
// #define int long long
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 510
#define M 5010
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

template<typename T> inline T Min(T a,T b){return a<b?a:b;}

struct node{
    int val,id;
    inline node(){}
    inline node(int val,int id) : val(val),id(id) {}
    inline bool operator < (const node &b)const{
        if(val!=b.val) return val>b.val;
        return id>b.id;
    }
};
priority_queue<node> q;

struct edge{
    int to,next,w;
    inline void Init(int to_,int ne_,int w_){
        to=to_;next=ne_;w=w_;
    }
}li[N<<1];
int head[N<<1],tail;

inline void Add(int from,int to,int w){
    li[++tail].Init(to,head[from],w);
    head[from]=tail;
}

int n,m,k,a[N],f[N][M<<1];

inline void Init(){
    read(n);read(m);read(k);
    for(int i=1;i<=m;i++){
        int from,to,w;read(from);read(to);read(w);
        Add(from,to,w);Add(to,from,w);
    }
    memset(f,INF,sizeof(f));
    for(int i=1;i<=k;i++){
        read(a[i]);f[a[i]][1<<(i-1)]=0;
    }
}

bool vis[N];
inline void dij(int s){
    memset(vis,0,sizeof(vis));
    while(q.size()){
        node top=q.top();q.pop();
        if(vis[top.id]) continue;
        vis[top.id]=1;
        for(int x=head[top.id];x;x=li[x].next){
            int to=li[x].to,w=li[x].w;
            if(f[to][s]<f[top.id][s]+w) continue;
            f[to][s]=f[top.id][s]+w;
            q.push(node(f[to][s],to));
        }
    }
}

inline void Dp(){
    for(int s=1;s<=(1<<k)-1;s++){
        for(int i=1;i<=n;i++){
            for(int sub=s&(s-1);sub;sub=s&(sub-1)){
                f[i][s]=Min(f[i][s],f[i][sub]+f[i][s^sub]);
            }
            if(f[i][s]!=INF) q.push(node(f[i][s],i));
            vis[i]=0;
            // printf("before:f[%d][%d]=%d
",i,s,f[i][s]);
        }
        dij(s);
        // for(int i=1;i<=n;i++){
        //     printf("f[%d][%d]=%d
",i,s,f[i][s]);
        // }
    }
    printf("%d
",f[a[1]][(1<<k)-1]);
}

signed main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    Init();Dp();
}
/*
6781325
*/
原文地址:https://www.cnblogs.com/TianMeng-hyl/p/15404172.html