最小树形图就是一个有向图,从根节点可以到达其他所有节点,而除根节点外的每个节点又有且仅有一个父节点,这样一张边权和最小的图就是最小树形图。
最小树形图有它特有的算法,朱刘算法。原理是先对除根节点以外的所有节点先找寻一条最小入边,接着判图中是否有有向环,如果有,将每个环缩成一点,再对这些点重新找最小入边,重复合并操作直至没有环。
我用的主要是从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 }