• [学习笔记]斯坦纳树


    处理一种这样的问题:

    斯坦纳树问题是组合优化问题,与最小生成树相似,是最短网络的一种。最小生成树是在给定的点集和边中寻求最短网络使所有点连通。而最小斯坦纳树允许在给定点外增加额外的点,使生成的最短网络开销最小。

    ——by 度娘

    简单来讲

    一个无向图,k个关键点,每个边有边权,求联通这k个点的最小代价

    最小生成树可以认为是斯坦纳树的特殊情况

    由于k个关键点的高度要求精确打击,只能用状压

    所以k一般最多到10

    n也不会太大

    方法:

    f[i][S]以i为根的树,连接了S集合关键点,的最少代价

    i为根,i只是一个表示,为了转移时候连边方便

    外层循环S

    第一个dp:f[i][S]=min(f[i][t]+f[i][S^t])直接通过自己原来的构造转移

    第二个dp:f[i][S]=min(f[i][S],f[k][S]+e[i][k])通过别的点连一条边过来

    第二个dp明显有环,所以spfa处理一下

    第一个dp更新完了之后,所有不是inf的点都加入queue,然后spfa

    可以证明最优解一定可以找到

    例题:

    [WC2008]游览计划

    模板题

    输出决策的时候,用pre记录前驱,注意区分两个dp转移过来方式的区别。pre不会有环,所以最后dfs一下 即可

    #include<bits/stdc++.h>
    #define il inline
    #define reg register int
    #define numb (ch^'0')
    using namespace std;
    typedef long long ll;
    il void rd(int &x){
        char ch;bool fl=false;
        while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
        for(x=numb;isdigit(ch=getchar());x=x*10+numb);
        (fl==true)&&(x=-x);
    }
    namespace Miracle{
    const int N=11;
    const int P=101;
    const int inf=0x3f3f3f3f;
    int mv[4][2]={{+1,0},{-1,0},{0,+1},{0,-1}};
    int f[N][N][1<<10];
    pair<int,int>pre[N][N][1<<10];
    int id[N][N];
    struct po{
        int x,y;
        po(){}
        po(int xx,int yy){
            x=xx;y=yy;
        }
    };
    queue<po>q;
    int n,m,k;
    int mp[N][N];
    char op[N][N];
    bool vis[N][N];
    void spfa(int s){
        while(!q.empty()){
            po now=q.front();q.pop();
            vis[now.x][now.y]=0;
            for(reg i=0;i<4;++i){
                int dx=now.x+mv[i][0],dy=now.y+mv[i][1];
                if(dx<1||dx>n) continue;
                if(dy<1||dy>m) continue;
                if(f[dx][dy][s]>f[now.x][now.y][s]+mp[dx][dy]){
                    f[dx][dy][s]=f[now.x][now.y][s]+mp[dx][dy];
                    pre[dx][dy][s]=make_pair(now.x,now.y);
                    if(!vis[dx][dy]){
                        vis[dx][dy]=1;
                        q.push(po(dx,dy));
                    }
                }
            }
        }
    }
    
    void check(int i,int j,int s){
        if(id[i][j]==0) op[i][j]='o';
        if(pre[i][j][s].first!=-1){
            if(pre[i][j][s].first==0){
                check(i,j,pre[i][j][s].second);
                check(i,j,s^pre[i][j][s].second);
            }else{
                check(pre[i][j][s].first,pre[i][j][s].second,s);
            }
        }else return;
    }
    int main(){
        rd(n);rd(m);
        for(reg i=1;i<=n;++i){
            for(reg j=1;j<=m;++j){
                rd(mp[i][j]);
                if(mp[i][j]==0){
                    ++k;id[i][j]=k;
                    op[i][j]='x';
                }
                
            }
        }    
        memset(f,inf,sizeof f);
        for(reg i=1;i<=n;++i){
            for(reg j=1;j<=m;++j){
                if(mp[i][j]==0){
                    f[i][j][1<<(id[i][j]-1)]=0;
                    pre[i][j][1<<(id[i][j]-1)]=make_pair(-1,-1);
                }
                f[i][j][0]=0;
                pre[i][j][0]=make_pair(-1,-1);
            }
        }
        for(reg s=1;s<(1<<k);++s){
            for(reg i=1;i<=n;++i){
                for(reg j=1;j<=m;++j){
                    for(reg t=(s-1)&s;t;t=(t-1)&s){
                        if(f[i][j][s]>f[i][j][t]+f[i][j][s^t]-mp[i][j]){
                            f[i][j][s]=f[i][j][t]+f[i][j][s^t]-mp[i][j];
                            pre[i][j][s]=make_pair(0,t);
                        }
                    }
                    if(f[i][j][s]!=inf) q.push(po(i,j)),vis[i][j]=1;
                }
            }
            spfa(s);
        }
        int tx,ty;
        int ans=inf;
        for(reg i=1;i<=n;++i){
            for(reg j=1;j<=m;++j){
                if(f[i][j][(1<<k)-1]<ans){
                    ans=f[i][j][(1<<k)-1];
                    tx=i;ty=j;            
                }
            }
        }
        check(tx,ty,(1<<k)-1);
        printf("%d
    ",ans);
        for(reg i=1;i<=n;++i){
            for(reg j=1;j<=m;++j){
                if(!op[i][j]) op[i][j]='_';
                putchar(op[i][j]);
            }puts("");
        }
        return 0;
    }
    
    }
    signed main(){
        Miracle::main();
        return 0;
    }
    
    /*
       Author: *Miracle*
       Date: 2019/1/6 19:03:36
    */
    View Code

    Peach Blossom Spring

    最后答案可能是一个森林

    先不管,先跑斯坦纳树一遍。S设为2k大小。因为答案最多加入2k个

    然后g[S]表示考虑S集合,最小代价。

    枚举子集合并斯坦纳树

    注意如果S中房屋的数量大于庇护所的数量,不能转移

  • 相关阅读:
    得不到的都能释怀
    个人读后感
    面向对象程序设计
    关于QQ的NABCD模型
    团队成员及分工
    软件工程结对项目--实用计算器的设计和制作
    实践作业2 个人项目作业
    github地址
    github心得体会
    人,绩效和职业道德
  • 原文地址:https://www.cnblogs.com/Miracevin/p/10230374.html
Copyright © 2020-2023  润新知