问题引入:
有某无向图,其有n个点,m条边,每条边边权w已知,求能使图连通的最小代价。
等价于 :
有一个联通图,它有n个点,把这个图去边,直到还剩n-1条边。如果现在这个图还是联通图,那么你就得到了一棵树,这棵树就是图的生成树,最小生成树就是一个图的所有生成树里这n-1条边的权值之和最小的。
人为去寻找最小连通方式!
解决方法:
思想: 贪心的按照边权从小到大加入。
过程:
1.将所有边按照边权从小到大排序。
2.选择一条当前边权最小且边的两个端点未连通的边,加入集合。
3.重复2操作;直到已选择n-1条边,算法结束。
时间复杂度O(mlogm)
分析:
- 对于1操作,直接使用sort排序即可。
- 对于2操作,难点是判断两个端点是否连通。
解决办法: 并查集
for(int i = 1; i <= n; i++) father[i] = i;
int find(int x)
{
if(father[x] != x) father[x] = find(father[x]);
return father[x];
}
例题 最小生成树
题目描述
如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出 orz
。
输入格式
第一行包含两个整数 \(N,M\),表示该图共有 \(N\) 个结点和 \(M\) 条无向边。
接下来 \(M\) 行每行包含三个整数 \(X_i,Y_i,Z_i\),表示有一条长度为 \(Z_i\) 的无向边连接结点 \(X_i,Y_i\)。
输出格式
如果该图连通,则输出一个整数表示最小生成树的各边的长度之和。如果该图不连通则输出 orz
。
样例 #1
样例输入 #1
4 5
1 2 2
1 3 2
1 4 3
2 3 4
3 4 3
样例输出 #1
7
提示
数据规模:
对于 \(20\%\) 的数据,\(N\le 5\),\(M\le 20\)。
对于 \(40\%\) 的数据,\(N\le 50\),\(M\le 2500\)。
对于 \(70\%\) 的数据,\(N\le 500\),\(M\le 10^4\)。
对于 \(100\%\) 的数据:\(1\le N\le 5000\),\(1\le M\le 2\times 10^5\),\(1\le Z_i \le 10^4\)。
样例解释:
所以最小生成树的总边权为 \(2+2+3=7\)。
AC 代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 200100;
struct node{
int u;
int v;
int w;
}e[N];
int father[N];
bool cmp(node a,node b)
{
return a.w < b.w;
}
int find(int x)
{
if(father[x] != x) father[x] = find(father[x]);
return father[x];
}
int ans,cnt;
int n,m;
int main()
{
ios::sync_with_stdio(false);
cout.tie(NULL);
cin>>n>>m;
for(int i = 1; i <= n; i++)
father[i] = i;
for(int i = 0; i < m ; i++)
cin>>e[i].u>>e[i].v>>e[i].w;
sort(e,e + m,cmp);
for(int i = 0 ;i < m; i++)
{
int fau = find(e[i].u);
int fav = find(e[i].v);
if(fau != fav)// 两点不连通
{
father[fau] = fav;//建立联通关系
ans += e[i].w;
cnt++;//边数 = 点数 - 1 则图恰好连通
if(cnt == n - 1) break;
}
}
if(cnt != n - 1) puts("orz");
else cout<<ans;
return 0;
}