• 图论--最小树形图朱刘算法模板


    最小树形图就是一个有向图,从根节点可以到达其他所有节点,而除根节点外的每个节点又有且仅有一个父节点,这样一张边权和最小的图就是最小树形图。

    最小树形图有它特有的算法,朱刘算法。原理是先对除根节点以外的所有节点先找寻一条最小入边,接着判图中是否有有向环,如果有,将每个环缩成一点,再对这些点重新找最小入边,重复合并操作直至没有环。

    我用的主要是从kuangbin巨巨的模板小改得到的昂。

    各种注释便于理解:

     1 #include<stdio.h>
     2 #include<string.h>
     3 
     4 const int maxn=1e5+5;
     5 const int maxm=1e5+5;
     6 const int INF=0x3f3f3f3f;
     7 //点数及边数
     8 
     9 int from[maxm],to[maxm],cost[maxm],cntm;    //这些是用来存边的
    10 int pre[maxn],id[maxn],vis[maxn],in[maxn];
    11 //pre是当前图中某个点所选的入边编号;id是当前图中某个点的编号,同一圈内的编号相同;in是当前图中某个点所选的入边权值
    12 
    13 void init(){
    14     cntm=0;
    15 }
    16 
    17 void add_mdst(int a,int b,int v){    //加边
    18     from[cntm]=a;
    19     to[cntm]=b;
    20     cost[cntm++]=v;
    21 }
    22 
    23 int mdst(int s,int n){
    24     int ans=0,u,v;
    25     while(1){
    26         memset(in,0x3f,sizeof(in));        //一开始先将所有点入边权赋为无穷大
    27         for(int i=0;i<cntm;++i){        //对于每一条边,如果它不在某个圈内,那么就用它来尝试更新点的入边
    28             if(from[i]!=to[i]&&cost[i]<in[to[i]]){
    29                 pre[to[i]]=from[i];
    30                 in[to[i]]=cost[i];
    31             }
    32         }
    33         for(int i=1;i<=n;++i){            //如果某个非根节点没有最小入边,则没有最小树形图,无解
    34             if(i!=s&&in[i]==INF){
    35                 return -1;
    36             }
    37         }
    38         int cnt=0;                        //圈的个数/缩完之后点的总数
    39         memset(id,-1,sizeof(id));
    40         memset(vis,-1,sizeof(vis));
    41         in[s]=0;
    42         for(int i=1;i<=n;++i){
    43             ans+=in[i];
    44             v=i;
    45             while(vis[v]!=i&&id[v]==-1&&v!=s){    //将一条链/圈标上同一标记
    46                 vis[v]=i;
    47                 v=pre[v];
    48             }
    49             if(v!=s&&id[v]==-1){        //当当前访问的不是长链连到根并且是一个没有标记过的圈,则标记这个圈
    50                 ++cnt;
    51                 for(u=pre[v];u!=v;u=pre[u])id[u]=cnt;
    52                 id[v]=cnt;
    53             }
    54         }
    55         if(!cnt)break;                    //无环,合并过程结束
    56         for(int i=1;i<=n;++i){            //将剩余点依次标序号
    57             if(id[i]==-1)id[i]=++cnt;
    58         }
    59         for(int i=0;i<cntm;){            //删去圈内的边;由于之前在遍历点的时候将入边权都加上了,所以在新图中将所有边权都减去原入边边权
    60             v=to[i];
    61             from[i]=id[from[i]];
    62             to[i]=id[to[i]];
    63             if(from[i]!=to[i])cost[i++]-=in[v];
    64             else{
    65                 --cntm;
    66                 cost[i]=cost[cntm];
    67                 to[i]=to[cntm];
    68                 from[i]=from[cntm];
    69             }
    70         }
    71         n=cnt;
    72         s=id[s];
    73     }
    74     return ans;
    75 }

    没有注释直接贴的板子:

     1 #include<stdio.h>
     2 #include<string.h>
     3 
     4 const int maxn=1e5+5;
     5 const int maxm=1e5+5;
     6 const int INF=0x3f3f3f3f;
     7 
     8 int from[maxm],to[maxm],cost[maxm],cntm;
     9 int pre[maxn],id[maxn],vis[maxn],in[maxn];
    10 
    11 void init(){
    12     cntm=0;
    13 }
    14 
    15 void add_mdst(int a,int b,int v){
    16     from[cntm]=a;
    17     to[cntm]=b;
    18     cost[cntm++]=v;
    19 }
    20 
    21 int mdst(int s,int n){
    22     int ans=0,u,v;
    23     while(1){
    24         memset(in,0x3f,sizeof(in));    
    25         for(int i=0;i<cntm;++i){
    26             if(from[i]!=to[i]&&cost[i]<in[to[i]]){
    27                 pre[to[i]]=from[i];
    28                 in[to[i]]=cost[i];
    29             }
    30         }
    31         for(int i=1;i<=n;++i){
    32             if(i!=s&&in[i]==INF){
    33                 return -1;
    34             }
    35         }
    36         int cnt=0;
    37         memset(id,-1,sizeof(id));
    38         memset(vis,-1,sizeof(vis));
    39         in[s]=0;
    40         for(int i=1;i<=n;++i){
    41             ans+=in[i];
    42             v=i;
    43             while(vis[v]!=i&&id[v]==-1&&v!=s){
    44                 vis[v]=i;
    45                 v=pre[v];
    46             }
    47             if(v!=s&&id[v]==-1){
    48                 ++cnt;
    49                 for(u=pre[v];u!=v;u=pre[u])id[u]=cnt;
    50                 id[v]=cnt;
    51             }
    52         }
    53         if(!cnt)break;
    54         for(int i=1;i<=n;++i){
    55             if(id[i]==-1)id[i]=++cnt;
    56         }
    57         for(int i=0;i<cntm;){
    58             v=to[i];
    59             from[i]=id[from[i]];
    60             to[i]=id[to[i]];
    61             if(from[i]!=to[i])cost[i++]-=in[v];
    62             else{
    63                 --cntm;
    64                 cost[i]=cost[cntm];
    65                 to[i]=to[cntm];
    66                 from[i]=from[cntm];
    67             }
    68         }
    69         n=cnt;
    70         s=id[s];
    71     }
    72     return ans;
    73 }
  • 相关阅读:
    H5测试
    mysql安装
    HTTP工作过程
    正则表达式常用方法
    20223
    视音频数据处理入门:RGB、YUV像素数据处理
    父母课堂——如何陪伴孩子过一个有意义的寒假
    c语言二维数组
    MobaXterm
    蓝牙介绍
  • 原文地址:https://www.cnblogs.com/cenariusxz/p/4811248.html
Copyright © 2020-2023  润新知