学习记录:最小生成树
最小生成树
最小生成树是无向图中额一个典型问题。问题模型可以用以下的方式描述:
给定无向图,要求连接所有的点,并求出此时最小的边长度总和。
模板题: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;
}