最小生成树:从图中选出一些边,构成一个树,包含图中所有点,且边的权值之和最小。

Kruskal

按边权对边排序,逐一检查,如果边的两点未联通,则采纳,否则跳过。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
struct MSTree{
private:
struct Edge{
ll u,v,w;
bool operator>(const Edge &e) const{
return w > e.w;
}
};
ll n;
DSU dsu;
priority_queue<Edge,vector<Edge>,greater<Edge>> Q;
public:
ll ans=0;
vector<vector<pll>> G; //最小生成树的邻接表
vector<ll> fa; //最小生成树i的父节点
MSTree(ll _n):n(_n),dsu(_n),G(_n+1),fa(n+1,0){}
void add_edge(ll u,ll v,ll w){ Q.push({u,v,w}); }
void solve(){
ans = 0;
while(!Q.empty()){
auto e = Q.top();Q.pop();
if(dsu.find(e.u) != dsu.find(e.v)){
dsu.merge(e.u,e.v);
G[e.u].emplace_back(e.v,e.w);
G[e.v].emplace_back(e.u,e.w);
ans += e.w;
}
}
//dfs求fa
auto DFS = [&](auto &&self, ll u=1,ll f=0) -> void{
fa[u] = f;
for(auto &p:G[u]){
ll v = p.first;
if(v == f) continue;
self(self,v,u);
}
};
DFS(DFS);
}
//判断图是否连通
bool connected(){
return dsu.size[dsu.find(1)] == n;
}
};
void solve(){
ll n,m;cin >> n >> m;
MSTree mst(n);
ll u,v,w;
FORLL(i,1,m){
cin >> u >> v >> w;
mst.add_edge(u,v,w);
}
mst.solve();
if(mst.connected()) cout << mst.ans << endl;
else cout << "orz\n";
}

Prim

从一个结点开始,加入边权最小的e(u,v),其中u在已选集合中,v在未选集合中,直到所有点都被选。

实现略