复制代码

MST

问题引入:
有某无向图,其有n个点m条边,每条边边权w已知,求能使图连通的最小代价。
等价于 :
有一个联通图,它有n个点,把这个图去边,直到还剩n-1条边。如果现在这个图还是联通图,那么你就得到了一棵树,这棵树就是图的生成树,最小生成树就是一个图的所有生成树里这n-1条边的权值之和最小的。
人为去寻找最小连通方式!
解决方法:
思想: 贪心的按照边权从小到大加入。
过程:
1.将所有边按照边权从小到大排序。
2.选择一条当前边权最小且边的两个端点未连通的边,加入集合。
3.重复2操作;直到已选择n-1条边,算法结束。
时间复杂度O(mlogm)
分析:

  1. 对于1操作,直接使用sort排序即可。
  2. 对于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;
}

prim

\(O(n^2)\) 找最小生成树 适用于稠密图

基本思路:

将加入最小生成树的点看作一个集合

那么寻找最小生成树的过程就是扩展集合。。 扩展集合时 找距离这个集合最近的,在外面的点 将其加入集合

从1开始扩展, 那么把与1相连的点 的\(d\) 改为边权,而非原设定的最大值,可以做到只搜相邻的,距离最近的。

struct node{
    int v,w;
};

std::vector<node> e[N];
int cnt = 0,ans = 0;

bool prim(int n, int s) {
    std::vector<int> d(n+1);
    for(int i = 0; i <= n; i++) d[i] = inf;
    d[s] = 0;
    std::vector<bool> vis(N,false);
    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] = true;
        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;
}
void solve()
{
    int n,m;
    std::cin >> n >> m;
    for(int i = 1,x,y,z; i <= m; i++) {
        std::cin >> x >> y >> z;
        e[x].push_back({y,z});
        e[y].push_back({x,z});
    }
    if(prim(n,1)) std::cout << ans << "\n";
    else std::cout << "orz" << "\n";
    
    return;
}

prim _ heap 优先队列优化 \(O(mlogn)\)

基于\(Dijkstra\) 的贪心

bool prim(int n, int s) {
    std::vector<int> d(n+1);
    for(int i = 0; i <= n; i++) d[i] = inf;
    d[s] = 0;
    std::vector<bool> vis(N,false);
    std::priority_queue< std::pair<int,int > > q;
    q.push({0,s});
    while(!q.empty()) {
        int u = q.top().second;
        q.pop();
        if(vis[u]) continue;
        vis[u] = true;
        ans += d[u]; cnt++;
        for(auto ed : e[u]) {
            int v = ed.v,w = ed.w;
            if(d[v] > w) {
                d[v] = w;
                q.push({-d[v],v});
            }
        }
    }
    return cnt == n;
}
posted @ 2023-10-03 16:07  Elgina  阅读(26)  评论(0)    收藏  举报