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

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

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

我用的主要是从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 }
原文地址:https://www.cnblogs.com/cenariusxz/p/4811248.html