题目情境#
题目链接:https://www.luogu.com.cn/problem/P3366 ↗
题目描述
如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出 orz。
输入格式
第一行包含两个整数 ,表示该图共有 个结点和 条无向边。
接下来 行每行包含三个整数 ,表示有一条长度为 的无向边连接结点 。
输出格式
如果该图连通,则输出一个整数表示最小生成树的各边的长度之和。如果该图不连通则输出 orz。
输入输出样例 #1
输入 #1
4 5
1 2 2
1 3 2
1 4 3
2 3 4
3 4 3plaintext输出 #1
7plaintext对于 的数据:,,。
样例解释:

所以最小生成树的总边权为 。
算法解析:#
Prim算法():

Kruscal算法():

Kruskal 求最小生成树:把所有边按边权升序排序,依次尝试加入,若边的两端点当前不连通(并查集不同集合)则加入,直到连成 条边;若最终边数不足 说明图不连通(输出 orz)。复杂度 。Prim 则从一点出发,每次用堆取当前到集合的最小边加点,适合稠密图。
Python代码实现#
N = 5010
d = [0] * N
vis = [0] * N
class Edge():
def __init__(self, v, w):
self.v = v
self.w = w
e = [[] for i in range(N)]
def prim(s):
global ans, cnt
for i in range(n + 1):
d[i] = int(1e9)
d[s] = 0
for i in range(1, n + 1):
u = 0
for j in range(1, n + 1):
if not vis[j] and d[j] < d[u]:
u = j
vis[u] = 1
ans += d[u]
if d[u] != 1e9:
cnt += 1
for ed in e[u]:
v = ed.v
w = ed.w
if d[v] > w:
d[v] = w
return cnt == n
n, m = map(int, input().split())
ans, cnt = 0, 0
for i in range(m):
a, b, c = map(int, input().split())
e[a].append(Edge(b, c))
e[b].append(Edge(a, c))
if not prim(1):
print("orz")
else:
print(ans)pythonKruskal 版(Python)#
class Edge():
def __init__(self, u, v, w):
self.u = u
self.v = v
self.w = w
def find(x):
if fa[x] == x:
return x
fa[x] = find(fa[x])
return fa[x]
def union(x, y):
fa[find(x)] = find(y)
def kruskal():
global ans, cnt
e.sort(key=lambda k:k.w)
for i in range(m):
x = find(e[i].u)
y = find(e[i].v)
if x != y:
union(x, y)
ans += e[i].w
cnt += 1
return cnt == n - 1
n, m = map(int, input().split())
fa = [i for i in range(n + 1)]
ans, cnt = 0, 0
e = []
for i in range(m):
u, v, w = map(int, input().split())
e.append(Edge(u, v, w))
if not kruskal():
print("orz")
else:
print(ans)pythonC++代码实现#
// Luogu P3366 【模板】最小生成树
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#define inf 1e9
using namespace std;
int n,m,a,b,c,ans,cnt;
const int N=5010;
struct edge{int v,w;};
vector<edge> e[N];
int d[N], vis[N];
bool prim(int s){
for(int i=0;i<=n;i++)d[i]=inf;
d[s]=0;
for(int i=1;i<=n;i++){
int u=0;
for(int j=1;j<=n;j++)
if(!vis[j]&&d[j]<d[u]) u=j;
vis[u]=1; //标记u已出圈
ans+=d[u];
if(d[u]!=inf) cnt++;
for(auto ed : e[u]){
int v=ed.v, w=ed.w;
if(d[v]>w) d[v]=w;
}
}
return cnt==n;
}
int main(){
cin>>n>>m;
for(int i=0; i<m; i++){
cin>>a>>b>>c;
e[a].push_back({b,c});
e[b].push_back({a,c});
}
if(!prim(1))puts("orz");
else printf("%d\n",ans);
return 0;
}cKruskal 版(C++)#
// Luogu P3366 【模板】最小生成树
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=200006;
int n, m;
struct edge{
int u,v,w;
bool operator<(const edge &t)const
{return w < t.w;}
}e[N];
int fa[N],ans,cnt;
int find(int x){
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
}
bool kruskal(){
sort(e,e+m);
for(int i=1;i<=n;i++)fa[i]=i;
for(int i=0; i<m; i++){
int x=find(e[i].u);
int y=find(e[i].v);
if(x!=y){
fa[x]=y;
ans+=e[i].w;
cnt++;
}
}
return cnt==n-1;
}
int main(){
cin>>n>>m;
for(int i=0; i<m; i++)
cin>>e[i].u>>e[i].v>>e[i].w;
if(!kruskal()) puts("orz");
else printf("%d\n",ans);
return 0;
}c