题目描述
如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出 orz
。
输入格式
第一行包含两个整数 N , M N,M N,M,表示该图共有 N N N 个结点和 M M M 条无向边。
接下来 M M M 行每行包含三个整数 X i , Y i , Z i X_i,Y_i,Z_i Xi,Yi,Zi,表示有一条长度为 Z i Z_i Zi 的无向边连接结点 X i , Y i X_i,Y_i Xi,Yi。
输出格式
如果该图连通,则输出一个整数表示最小生成树的各边的长度之和。如果该图不连通则输出 orz
。
样例 #1
样例输入 #1
4 5
1 2 2
1 3 2
1 4 3
2 3 4
3 4 3
样例输出 #1
7
提示
数据规模:
对于 20 % 20\% 20% 的数据, N ≤ 5 N\le 5 N≤5, M ≤ 20 M\le 20 M≤20。
对于 40 % 40\% 40% 的数据, N ≤ 50 N\le 50 N≤50, M ≤ 2500 M\le 2500 M≤2500。
对于 70 % 70\% 70% 的数据, N ≤ 500 N\le 500 N≤500, M ≤ 1 0 4 M\le 10^4 M≤104。
对于 100 % 100\% 100% 的数据: 1 ≤ N ≤ 5000 1\le N\le 5000 1≤N≤5000, 1 ≤ M ≤ 2 × 1 0 5 1\le M\le 2\times 10^5 1≤M≤2×105, 1 ≤ Z i ≤ 1 0 4 1\le Z_i \le 10^4 1≤Zi≤104。
样例解释:
所以最小生成树的总边权为 2 + 2 + 3 = 7 2+2+3=7 2+2+3=7。
Kruskal
点个免费的赞吧谢谢ヽ( ̄ω ̄( ̄ω ̄〃)ゝ
#include <bits/stdc++.h>
using namespace std;
const int N = 5005,M = 2e5+1;
struct Edge{int u,v,w;}edge[M]; //用最简单且最省空间的结构体数组存边
bool cmp(Edge a, Edge b){ return a.w < b.w;} //从小到大排序
int s[N]; //并查集
int find_set(int x){ //查询并查集,返回x的根
if(x != s[x])s[x] = find_set(s[x]);//路径压缩
return s[x];
}
int n,m; // n个点,m条边
void kruskal(){
sort(edge+1, edge+m+1, cmp); //对边做排序
for(int i=1; i<=n; i++) s[i]=i; //并查集初始化
int ans = 0, cnt=0; //cnt: 计数已加入MST的边数
for(int i=1; i<=m; i++){ //贪心:逐一加入每条边
if(cnt == n-1)break; //小优化:不要也行
int xset = find_set(edge[i].u); //边的前端点u属于哪个集?
int yset = find_set(edge[i].v); //边的后端点v属于哪个集?
if(xset == yset) continue; //属于同一个集:产生了环,丢弃
else{ //不属于同一个集
ans += edge[i].w; //增加MST长度
s[xset]= yset; //合并并查集
cnt++; //更新MST中的边数
}
}
if(cnt == n-1) cout << ans; //n-1条边
else cout << "orz"; //图不是连通的
}
int main(){
cin >> n >> m;
for(int i=1; i<=m; i++) cin >> edge[i].u >> edge[i].v >> edge[i].w;
kruskal();
return 0;
}
Prim:
点个免费的赞吧谢谢ヽ( ̄ω ̄( ̄ω ̄〃)ゝ
#include <bits/stdc++.h>
using namespace std;
const int N=5005,M = 2e5+1;
struct edge{ //记录边
int to, w;
edge(int a,int b){ to = a, w = b;} //赋值
};
vector <edge>G[M];
struct node {
int id, dis; //id:点;dis:边
node(int a, int b){ id = a, dis = b;} //赋值
bool operator < (const node &u) const { return dis > u.dis; }
};
int n, m;
bool done[N]; //done[i]=ture: 表示点i已经在MST中
void prim() { //对比dijkstra: 求s到其他所有点的最短路
int s = 1; //从任意点开始,例如从1开始
for(int i =1;i<=N;i++) done[i]=false; //初始化
priority_queue<node> q;
q.push(node(s, 0)); //从s点开始处理队列
int ans=0,cnt=0;
while (!q.empty()) {
node u = q.top(); q.pop(); //pop出距集合U最近的点u
if (done[u.id]) continue; //丢弃已经在MST中的点,有判圈的作用
done[u.id] = true; //标记
ans += u.dis;
cnt++; //统计点数
for (int i = 0; i< G[u.id].size(); i++) { //检查点u的所有邻居
edge y = G[u.id][i]; //一个邻居y
if (done[y.to]) continue; //丢弃已经在MST中的点
q.push(node(y.to, y.w)); //扩展新的邻居,放进优先队列
}
}
if(cnt == n) cout << ans; //cnt=n个点。注意在kruskal代码中cnt是边数
else cout << "orz";
}
int main() {
cin>>n>>m;
for(int i=1; i<=m; i++) {
int a,b,w; cin>>a>>b>>w;
G[a].push_back(edge(b,w)); G[b].push_back(edge(a,w)); //双向边
}
prim();
return 0;
}