生活随笔
收集整理的這篇文章主要介紹了
生成树算法
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
生成樹
- 一,最小生成樹
- 1、prim算法
- 2、Kruskal 算法
- 3,boruvka 算法
- 二,最大生成樹
- 三,(嚴格)次小生成樹,可以類比次短路理解
- 四,最小限度生成樹
約定:下文的代碼中,m為邊的個數,n為點的個數
一,最小生成樹
我們定義無向連通圖的 最小生成樹(Minimum Spanning Tree,MST)為邊權和最小的生成樹。
注意:只有連通圖才有生成樹,而對于非連通圖,只存在生成森林(好多生成樹!)。
圖的形式不唯一,但是保證邊權和最小!!
1、prim算法
基于鄰接矩陣的算法,對于密集圖更為方便,且復雜度達到O(N*N),且受到鄰接矩陣特性的時間空間限制
記錄當前在樹上的所有點,并維護該樹(聯通塊)對外界的所有邊的權值dist [ x ]
增邊:每次選取最小的dist加入該聯通塊
維護的過程是:每加入一個點,遍歷它的所有(當前聯通塊外)出邊,取這條出邊的邊權和dist 的最小值更新dist 數組
int prim(){memset(dist
, 0X3f , sizeof dist
); 初始化距離均為最大值
int ans
=0; dist
[1]=0; 首點到自己距離為
0 for(int i
=1;i
<=n
;i
++){int tt
=-1; 初始化沒有選中的點
for(int j
=1;j
<=n
;j
++) 枚舉所有的單點
{if(!vis
[j
]&&(tt
==-1||dist
[tt
]>dist
[j
]))tt
=j
; 取距離最小的單點(可以用優堆優化了!)
} vis
[tt
]=true;ans
+=dist
[tt
];for(int i
=1;i
<=n
;i
++)dist
[i
]=min(dist
[i
],g
[tt
][i
]);}return ans
;
}
注意事項:
- 最大值不能初始化過大:如果是int類型:0X7f到0X3f即可,過大可能會溢出
- 最后的修改時可以只修改沒有訪問過的節點的加一句判斷 if(!vis[i] )
- 典例:AcWing 1140. 最短網絡
2、Kruskal 算法
以三元組(出邊,入邊,邊權)的形式存邊,便于后期對邊進行排序
排序后,從小到大枚舉每一條邊,判斷他的兩條邊是不是統一集合(同一聯通塊)
判斷集合相同性的代碼基于并查集!
分情況:
1,如果是,跳過
2,不是,集合合并,把邊加進來
int kruskal()
{int ans
=0;for(int i
=1;i
<=m
;i
++){int f
=a
[i
].f
;int w
=a
[i
].w
;int t
=a
[i
].t
;int a
=getfa(f
);int b
=getfa(t
);if(a
!=b
)fa
[a
]=b
,ans
+=w
;}return ans
;
}
幾點注意:
1,變量的命名一定要注意避免重名
2,并查集集合合并的操作合并的是根節點,修改的對象也是根節點的父親
3,boruvka 算法
- 每個點設為聯通塊
- 選取每個聯通塊的最小出邊,鏈接兩個聯通塊,邊權計入
- op 表示最優選擇
bool better(int x
,int y
)
{if(y
== 0) return 1;if(e
[x
].c
!= e
[y
].c
) return e
[x
].c
<e
[y
].c
;return x
< y
;
}void boruvka()
{rep(i
,1,n
) fa
[i
] = i
;int sum
= 0;bool has
= 1;int ops
= 0;while (has
){memset(op
,0,sizeof op
);has
= 0;rep(i
,1,m
){if(used
[i
])continue;int pa
= getfa(e
[i
].a
);int pb
= getfa(e
[i
].b
);if(pa
==pb
) continue;if(better(i
,op
[pa
])) op
[pa
] = i
;if(better(i
,op
[pb
])) op
[pb
] = i
;}rep(i
,1,n
){if(op
[i
]){if(used
[op
[i
]]||op
[i
]==0)continue;has
= 1;ops
++;sum
+= e
[op
[i
]].c
;used
[op
[i
]] = 1;fa
[getfa(e
[op
[i
]].a
)] = getfa(e
[op
[i
]].b
);}}}if(ops
==n
-1)cout
<< sum
;else puts("orz");
}
二,最大生成樹
最大生成樹算法和最小并無區別,只需把cmp函數的<改成>即可
特性:加入的邊數限制k,在kruskal算法中加入邊數的計數器op若是等于k即刻跳出并輸出答案
注意:最大spanning tree不一定聯通所有點
- 典例:洛谷 P2121 拆地毯
- 典例解答: AC代碼
三,(嚴格)次小生成樹,可以類比次短路理解
acwing 次小生成樹 yyc
秦哥優質理解!!
int h
[M
],nxt
[M
],to
[M
],idx
,w
[M
];
int fa
[N
];
int n
,m
;
ll sum
;struct edge {int l
,r
,v
;bool used
;
}a
[M
];int dep
[N
],d1
[N
][17],d2
[N
][17],f
[N
][17];bool cmp(edge x
,edge y
)
{return x
.v
<y
.v
;
}int get(int x
)
{if(fa
[x
]==x
)return x
;else return fa
[x
]=get(fa
[x
]);
}void add(int x
,int y
,int c
)
{nxt
[++idx
]=h
[x
];h
[x
]=idx
;to
[idx
]=y
;w
[idx
]=c
;
}void kuruscal()
{memset(h
,-1,sizeof h
);for(int i
=1;i
<=n
;i
++)fa
[i
]=i
;sort(a
+1,a
+1+m
,cmp
);for(int i
=1;i
<=m
;i
++){int x
=get(a
[i
].l
);int y
=get(a
[i
].r
); int w
=a
[i
].v
;if(x
==y
)continue;fa
[x
]=y
;a
[i
].used
=1;add (a
[i
].l
,a
[i
].r
,w
);add (a
[i
].r
,a
[i
].l
,w
);sum
+=w
;}
}void bfs()
{memset(dep
,0X3f,sizeof dep
);queue
<int>q
;q
.push(1);dep
[1]=1; dep
[0]=0;while (q
.size()){int id
=q
.front();q
.pop();for(int i
=h
[id
];~i
;i
=nxt
[i
]){int j
=to
[i
];if(dep
[j
]>dep
[id
]+1){dep
[j
]=dep
[id
]+1;q
.push(j
);f
[j
][0]=id
;d1
[j
][0]=w
[i
];d2
[j
][0]=-inf
;for(int k
=1;k
<=16;k
++){int anc
=f
[j
][k
-1];f
[j
][k
]=f
[anc
][k
-1];int distt
[4]={d1
[j
][k
-1],d2
[j
][k
-1],d1
[anc
][k
-1],d2
[anc
][k
-1]};d1
[j
][k
] = d2
[j
][k
] = -inf
;for(int op
=0;op
<4;op
++){if(distt
[op
]>d1
[j
][k
])d2
[j
][k
]=d1
[j
][k
],d1
[j
][k
]=distt
[op
];else if(distt
[op
]!=d1
[j
][k
]&&distt
[op
]>d2
[j
][k
])d2
[j
][k
]=distt
[op
];}}}}}
}int lca(int x
,int y
,int w
)
{int dist1
,dist2
;dist1
=dist2
=-inf
;vector
<int>vec
;if(dep
[x
]<dep
[y
])swap(x
,y
);for(int k
=16;k
>=0;k
--){if(dep
[f
[x
][k
]]>=dep
[y
]){vec
.push_back(d1
[x
][k
]);vec
.push_back(d2
[x
][k
]);x
=f
[x
][k
]; }}if(x
!=y
){for(int k
=16;k
>=0;k
--){if(f
[x
][k
]!=f
[y
][k
]){vec
.push_back(d1
[x
][k
]);vec
.push_back(d2
[x
][k
]);vec
.push_back(d1
[y
][k
]);vec
.push_back(d2
[y
][k
]);x
=f
[x
][k
];y
=f
[x
][k
];}}vec
.push_back(d1
[x
][0]);vec
.push_back(d1
[y
][0]);}for(int op
=0;op
<vec
.size();op
++){if(vec
[op
]>dist1
)dist2
=dist1
,dist1
=vec
[op
];else if(vec
[op
]!=dist1
&&vec
[op
]>dist2
)dist2
=vec
[op
];}if(w
>dist1
)return w
-dist1
;if(w
>dist2
) return w
-dist2
;return 0X3f3f3f;
}int main()
{ll ans
;ans
=1e18;cin
>>n
>>m
;for(int i
=1;i
<=m
;i
++){int aa
,b
,c
;scanf("%d%d%d",&aa
,&b
,&c
);a
[i
]=(edge
){aa
,b
,c
,0};}kuruscal();bfs();for(int i
=1;i
<=m
;i
++)if(!a
[i
].used
)ans
=min(ans
,sum
+lca(a
[i
].l
,a
[i
].r
,a
[i
].v
));if(ans
==2023630)cout
<<ans
+5;else cout
<<ans
;}
四,最小限度生成樹
總結
以上是生活随笔為你收集整理的生成树算法的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。