我一直有点搞不清kruskal和prim的区别,但是kruskal就是简单粗暴的那个!就是一个图G,n个结点,我们对边进行排序,找到n-1条边就可以了(当然前提是这n-1条边不会连成环,如果连成环就换一个)。
前面的步骤都好说,主要是一个结构体Edge,让后对这个结构体的数组进行排序,然后就可以选择最小的。
主要是怎么判断有没有连城环,其实可以做一个已经连接的结点的vector,然后判断,如果这条边的起始和终止都已经属于了这个vector,说明再加这条边进去,就是会形成一个环了,这个时候就要机智的跳过,选择下一条边。
不过这个方法还是蠢了点,因为你得遍历寻找,会慢很多,然后再想想本质,其实可以看成,组成这一条边的两端的结点的集合的一个合并。因为这条边像是作为一个桥,把这些连接起来了,这个时候其实可以用到并查集,并查集就是判断连通分支数的,我们这个时候需要连通分支数>2,就是连接这条边的两个端点结点分别属于不同的集合,这样连接起来就不会有环出现,如果两个端点已经属于同一个连通分支,那相当于已经存在路,那怎么再加一条边呐,再加一条边就形成环了。
好了还有最后一个要点,那就是怎么判断是否能形成最小的生成树呐,其实得保证我们的图,是连通的,这样一定会有连通分支数,那这个可以在形成了并查集那个地方再判断一下。
#include <stdio.h>
#include <iostream>
#include <vector>
#include <algorithm>
#pragma warning(disable:4996);
//Kruskal算法,就是把Edge边进行排序,排序以后通过并查集进行选择
using namespace std;
struct Edge
{
int from;
int to;
int val;
};
const int maxn = 100010;
vector<Edge> edges;
int Father[maxn];
int JudgeFather[maxn];
int visit[maxn] = { 0 };
//N个结点,M条无向边
int N, M;
int weight = 0;
bool cmp(Edge a, Edge b)
{
return a.val < b.val;
}
void initFather(int n)
{
for (int i = 0; i <= n; i++) {
Father[i] = i;
JudgeFather[i] = i;
}
}
int Find(int x)
{
if (x != Father[x]) {
Father[x] = Find(Father[x]);
}
return Father[x];
}
void Union(int a, int b)
{
a = Find(a);
b = Find(b);
Father[a] = b;
}
int main()
{
scanf("%d %d", &N, &M);
initFather(N);
if (M < N - 1)
{
printf("orz");
}
for (int i = 0; i < M; i++) {
Edge e;
scanf("%d %d %d", &e.from, &e.to, &e.val);
edges.push_back(e);
}
sort(edges.begin(), edges.end(), cmp);
int chooseEdge = 0;
for (int i = 0; i < M; i++)
{
if (Find(edges[i].from) != Find(edges[i].to)) {
Union(edges[i].from, edges[i].to);
weight = weight + edges[i].val;
chooseEdge++;
}
}
if (chooseEdge == N-1) {
printf("%d", weight);
}
else {
printf("orz");
}
}