学习记录:最小生成树

最小生成树

最小生成树是无向图中额一个典型问题。问题模型可以用以下的方式描述:

给定无向图,要求连接所有的点,并求出此时最小的边长度总和。

模板题:P3366 【模板】最小生成树

前置知识:并查集,贪心,存图方法

prim算法

简介:

是对的贪心算法。从任意一点开始,选取距离该点最近的一点加入集合中,再从剩下的点中找出距离集合最近的一点,加入到集合中;重复以上过程,直到所有点都在集合中。(有点像Dijstra)

但与Dijstra不同的是,prim算法不需要松弛操作。最终记录在dis数组上的不是出发点到目标点的距离,而是集合到目标点的最短距离。顺带一提,prim不需要并查集的知识。

#include <bits/stdc++.h>
using namespace std;

#define rp1(i, a, b) for (int i = a; i < b; i++)
#define rp2(i, a, b) for (int i = a; i <= b; i++)
typedef long long ll;
typedef unsigned long long ull;

int mod = 9973;
const int INF = 0x3f3f3f3f;
const int maxn = 5e3 + 10;

int Map[maxn][maxn];
int vis[maxn];
int dis[maxn];
int n, m, flag = 0;

void Prim()
{
	rp2(i, 1, n)
		dis[i] = Map[1][i]; //dis数组初始化
	int res = 0;			//最终答案
	dis[1] = 0;
	vis[1] = 1;
	rp1(k, 0, n - 1) //循环n-1次,因为连接n个点最少需要n-1条边
	{
		int u = INF;
		int pos;
		rp2(i, 1, n)
		{
			if (!vis[i] && u > dis[i])
			{
				pos = i; //找到最短点,并记录
				u = dis[i];
			}
		}
		if (u == INF) //不是连通图
		{
			cout << "orz" << endl;
			exit(0);
		}
		vis[pos] = 1;
		res += u;
		rp2(i, 1, n)
		{
			if (!vis[i] && dis[i] > Map[pos][i]) //与Dijstra不同的地方
				dis[i] = Map[pos][i];
		}
	}
	printf("%d\n", res);
}

int main()
{
	scanf("%d%d", &n, &m);
	rp2(i, 1, n)
		rp2(j, 1, n)
			Map[i][j] = (i == j) ? 0 : INF;
	for (int i = 1; i <= m; i++)
	{
		int x, y, z;
		scanf("%d%d%d", &x, &y, &z);
		Map[x][y] = Map[y][x] = min(z, Map[x][y]); //洛谷这个相当坑,路径可能是重的,要取最小值
	}
	Prim();
}

kruskal 算法

简介:

对边进行贪心操作。在所有边中寻找最短的边,加入集合中;如果形成闭环,则去除该边;直到最小生成树建立完成。

要注意的是,在kruskal中判断重边,应用了并查集。每当选定一条边时,判断当前的两个端点是否在一个集合里。如果是,则形成了闭环,这条边不要。

#include <bits/stdc++.h>
using namespace std;

#define rp1(i, a, b) for (int i = a; i < b; i++)
#define rp2(i, a, b) for (int i = a; i <= b; i++)
typedef long long ll;
typedef unsigned long long ull;

int mod = 9973;
const int INF = 0x3f3f3f3f;
const int maxn = 2e5 + 10;

int S[maxn];
struct Edge
{
	int x, y, z;
} edge[maxn];
bool cmp(Edge a, Edge b) { return a.z < b.z; }
int find(int u)
{
	while (u != S[u])
		u = S[u] = S[S[u]];
	return u;
}
int n, m;

int kruskal()
{
	int ans = 0;
	for (int i = 1; i <= n; i++)
		S[i] = i;
	sort(edge + 1, edge + 1 + m, cmp);
	for (int i = 0; i < m; i++)
	{
		int b = find(edge[i].x);
		int c = find(edge[i].y);
		if (b == c)
			continue;
		S[c] = b;
		ans += edge[i].z;
	}
	return ans;
}
int main()
{
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= m; i++)
		scanf("%d%d%d", &edge[i].x, &edge[i].y, &edge[i].z);
	printf("%d\n", kruskal());
	return 0;
}
posted @ 2020-07-27 16:54  Salty_Fish  阅读(116)  评论(0编辑  收藏  举报