最小生成树总结

 目前还不够全面,将持续更新...

Prim算法:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
//自环和重边(取边权最小值)不会对prim算法产生影响 
//对于已经存在的边,将它们的边权赋值为0即可,之后还是按原来的方法求解 
int mp[5005][5005], dis[5005];
bool vis[5005];
int n, m, ans;

bool prime()
{
	memset(vis, false, sizeof vis);
	memset(dis, 0x3f, sizeof dis);
	dis[1] = 0;//任取一起点 
	for(int i = 1; i <= n; i++)
	{
		int now = -1, _min = 0x3f3f3f3f;
		for(int j = 1; j <= n; j++)
		{
			if(!vis[j] && dis[j] < _min)
			{
				_min = dis[j];
				now = j;
			}
		}
		if(now == -1)
			return false;
		vis[now] = true;
		ans += _min;
		//接下来更新不在集合中的各点距离集合的最短距离 
		for(int j = 1; j <= n; j++)
		{
			if(!vis[j] && dis[j] > mp[now][j])
				dis[j] = mp[now][j];
		}
	}
	return true;
}

signed main()
{
	int u, v, w;
	cin >> n >> m;
	//初始化为无穷大,表示各点之间都独立 
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++)
			mp[i][j] = 0x3f3f3f3f; 
	//建图 
	for(int i = 1; i <= m; i++)
	{
		scanf("%d%d%d", &u, &v, &w);
		if(!mp[u][v])
			mp[u][v] = mp[v][u] = w;
		else//去重边 
			mp[u][v] = mp[v][u] = min(w, mp[u][v]);
	} 
	if(prime())
		cout << ans;
	else
		puts("impossible");
	return 0;
}

Kruskal算法:

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
//因为使用了并查集,所以自环没有影响,由于进行了排序,所以重边也没影响(优先选取权最小的) 
//对于已经存在的边,把权值为0的这些边加入边集数组即可,排序后一定先选它们
int n, m, fa[5005], ans;
struct node
{
	int u, v, w;
}edge[200005];

bool cmp(node a, node b)
{
	return a.w < b.w;
}

void init()
{
	for(int i = 1; i <= n; i++)
		fa[i] = i;
}

int find(int x)
{
	if(x == fa[x])
		return x;
	return fa[x] = find(fa[x]);
}

void merge(int x, int y)
{
	int fx = find(x), fy = find(y);
	if(fx != fy)
		fa[fx] = fy;
}

bool kruskal()
{
	init();
	int t = 0;
	sort(edge+1, edge+m+1, cmp);
	for(int i = 1; i <= m; i++)
	{
		if(find(edge[i].u) != find(edge[i].v))
		{
			t++;
			merge(edge[i].u, edge[i].v);
			ans += edge[i].w;
		}
		if(t == n-1)
			return true;
	}
	return false;
}

signed main()
{
	cin >> n >> m;
	for(int i = 1; i <= m; i++)
		scanf("%d%d%d", &edge[i].u, &edge[i].v, &edge[i].w);
	if(kruskal())
		cout << ans;
	else
		puts("impossible");
	return 0;
}

堆优化的Prim算法:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <utility>
#include <queue>
#define pii pair<int, int> 
#define inf 0x3f3f3f3f
using namespace std;
//时间复杂度O(nloge)
//不受自环、重边影响(实验得出) 
//适合于边多点少的情况 
//dis依然保存到集合的最短距离,通过新加入集合的点来更新 
//优先队列可以优化找距离集合最近点的for循环
//链式前向星可以优化更新所有点dis的for循环,因为之前邻接矩阵存图,并不清楚各点邻接关系,需要遍历所有点,而现在只需遍历相邻点 
int cnt, n, m, ans, num;
int head[5005], dis[5005];
bool vis[5005];
struct edges
{
	int v, w, next;
}edge[400005];//别忘了二倍空间 

void init()
{
	cnt = 0;
	ans = 0;
	for(int i = 1; i <= n; i++)
		head[i] = 0, dis[i] = inf, vis[i] = false;
}

void add(int u, int v, int w)
{
	edge[++cnt].v = v;
	edge[cnt].w = w;
	edge[cnt].next = head[u];
	head[u] = cnt;
}

bool prime()
{
	//在已知边权都为正值时,也可以用大根堆优先队列,只是加入边权时取负数,这样就模拟了小根堆 
	priority_queue<pii, vector<pii>, greater<pii> > a;
	dis[1] = 0;
	a.push(make_pair(0, 1));//因为按第一关键词升序排序 
	while(!a.empty() && num < n)
	{
		int d = a.top().first;
		int now = a.top().second;
		a.pop();
		if(vis[now])
			continue;//一个点可能被加入队列多次,只处理最小的那个,别的都可以跳过 
		ans += d;
		num++;//加入集合的点数 
		vis[now] = true;//把u加入集合 
		for(int i = head[now]; i; i = edge[i].next)//每个点的度都要遍历,一共遍历2*e次 
		{
			if(dis[edge[i].v] > edge[i].w)//与朴素算法的不同,这里是用新加入的点去更新邻接的点 
			{
				dis[edge[i].v] = edge[i].w;
				a.push(make_pair(dis[edge[i].v], edge[i].v));
			}
		} 
	}
	if(num == n)
		return true;
	return false;
}

signed main()
{
	int u, v, w;
	cin >> n >> m;
	init();
	for(int i = 1; i <= m; i++)
	{
		scanf("%d%d%d", &u, &v, &w);
		add(u, v, w);
		add(v, u, w);
	}
	if(prime())
		cout << ans;
	else
		puts("impossible");
	
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值