最小生成树:在连通网的所有生成树中,所有边的代价和最小的生成树,称为最小生成树。
分析:从一个初始点开始,每次获取与该点直连的点,并与之前获取的可连接的边比较,得到最小边,然后将连接的点并入集合(以后的点不能在包括他,要不然会形成回路),直到遍历完所有顶点纠结束。
代码:
#include<iostream>
const int INF = 10000;
using namespace std;
const int N = 6;
bool visit[N];
int dist[N] = { 0 };
int g[N][N] = { { INF, 6, 1, 5, INF, INF }, //INF代表两点之间不可达
{ 6, 5, INF, INF, 3, INF },
{ 1, 5, INF, 5, 6, 4 },
{ 5, INF, 5, INF, INF, 2 },
{ INF, 3, 6, INF, INF, 6 },
{ INF, INF, 4, 2, 6, INF }
};
int prim(int cur){
int index = cur;
int sum = 0;
for (int i = 0; i < N; i++){
dist[i] = g[cur][i];//获取所有点与第一个点的距离
}
memset(visit, false, sizeof(visit));
visit[cur] = true;//第一个点不能再包括
cout << index << " ";//输出第一个点
for (int i = 1; i < N; i++){
int minor = INF;
for (int j = 0; j < N; j++){//寻找最短边
if (!visit[j] && dist[j] < minor){
minor = dist[j];
index = j;
}
}
visit[index] = true;//最短边对应的另一个点纳入集合
cout << index << " ";
sum += minor;//最小树的总距离长度
for (int j = 0; j < N; j++){
if (!visit[j] && dist[j]>g[index][j]){//更新当前包括之前可连的点(最短),当前这个点a与其他点b的距离可能比之前的其他点c与点b的距离要短,所以要更新。
dist[j] = g[index][j];
}
}
}
cout << endl;
return sum;
}
int main(void){
cout << prim(0) << endl;
system("pause");
return 0;
}
给自己留个坑,还有个kruskal算法也是求最小树的,大体思路就是用并查集来做,我现在还没开始写,之后补上。
思路:
把所有的线段以不降序列排序,然后从小到大遍历线段的两个节点,若两个节点不属于同一个集合,即不会形成回路,可以纳入集合,方法使用并查集的相关操作,
写好了,代码附上:
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 100;
struct node {
int a, b, l;//线段的头结点和尾节点和长度
bool operator<(node n1) {
return n1.l > this->l;//以长度做不降序列
}
};
node arr[N];
int parent[N], dist[N];//父节点和节点数
int m, n;
void creat() {
for (int i = 0; i < n; i++) {
parent[i] = i;
dist[i] = 1;
}
}
int find(int cur) {//查找根节点值
if (parent[cur] != cur)parent[cur] = find(parent[cur]);
return parent[cur];
}
void _union(int x, int y) {//合并节点
int r1, r2;
r1 = find(x); r2 = find(y);
if (dist[r1] > dist[r2]) {//按秩合并,将节点数少的放在节点数多的下面
parent[r2] = r1;
}
else if (dist[r1] < dist[r2]) {
parent[r1] = r2;
}
else {
parent[r1] = r2;
dist[r2]++;
}
}
void kruskal() {
creat();
int sum = 0;
for (int i = 0; i < n; i++) {
int x = arr[i].a, y = arr[i].b;
if (find(x) != find(y)) {//如果两节点不在同一集合就可以合并
sum += arr[i].l;
_union(x, y);
}
}
cout << sum<<endl;
}
int main(void) {
cin >> m >> n;
for (int i = 0; i < n; i++) {
cin >> arr[i].a >> arr[i].b >> arr[i].l;
}
sort(arr, arr + n);
kruskal();
system("pause");
return 0;
}
2019.3.6更新
在kruskal的代码里面,并查集的部分,dist数组我之前说的是节点数,现在想一想准确讲应该是这个集合的层数才对,集合a比集合b层数少,就将a放在b的下面,如果a和b相等,把a放在b的下面,会造成层数+1,这也应对了dist数组的自加。