一、Prim,Kruskal算法简介
这两个最小生成树的算法思路都比较容易理解,下面给大家说一下我对这两个算法的理解和相关的代码实现,也是给自己以后温习最小生成树算法的时候有个参考的例子。
1. Kruskal 算法
对于上面的无向图B来说,克鲁斯卡尔算法的思路是假设起初各个点之间没有边相连,只有A~G这7个点,然后我按照边的权重从小到大排序,依次把边加入,如果当前边加入后不会构成环,那么当前边就是最小生成树的一部分,否则舍弃该条边。
用伪代码表示:
----用 Edges 保存各条边;
----Sort(Edges); --> 按照边的权重从小到达排序
----For edge in Edges: --> 检验每条边
--------IF Check(edge): --> 检测加入edge后会不会构成环
------------Join(edge); --> 如果不会产生环,则加入这条边
--------ELSE:
------------Delete(edge); --> 否则该条边不合法,删除这条边
2. Prim 算法
对于上面的的无向图B来说,普里姆算法的核心思路就是在维护一个顶点的集合V和一个边的集合E,这个V集合表示当前最小生成树是由哪几个顶点构成的,边的集合E表示最小生成树是由哪几条边构成的。
起初随意向V集合加入一个顶点即可,然后从与当前集合V相连的各个边中选一个最短的边,将这条最短边加入E,边上的另外一个顶点加入V集合,直至所有的顶点全部加入集合,最小生成树也就产生了。
用伪代码表示:
----define V //用V表示当前最小生成树包含哪些顶点,初始值为空
----define E //用E表示当前最小生成树包含哪些边,初始值为空
----V <- A //把顶点A加入顶点集
----while(check(V)) //检验V集合是否包含了所有的顶点
--------edge = chose(V) //选出一个与集合V相连的最短的一条边
--------E<-edge // 将这条边加入边的集合E
--------V<-edge.to // 将这条边到达的顶点加入集合V
二、HDU-1301 最小生成树算法应用
问题描述
The Head Elder of the tropical island of Lagrishan has a problem. A burst of foreign aid money was spent on extra roads between villages some years ago. But the jungle overtakes roads relentlessly, so the large road network is too expensive to maintain. The Council of Elders must choose to stop maintaining some roads. The map above on the left shows all the roads in use now and the cost in aacms per month to maintain them. Of course there needs to be some way to get between all the villages on maintained roads, even if the route is not as short as before. The Chief Elder would like to tell the Council of Elders what would be the smallest amount they could spend in aacms per month to maintain roads that would connect all the villages. The villages are labeled A through I in the maps above. The map on the right shows the roads that could be maintained most cheaply, for 216 aacms per month. Your task is to write a program that will solve such problems.
The input consists of one to 100 data sets, followed by a final line containing only 0. Each data set starts with a line containing only a number n, which is the number of villages, 1 < n < 27, and the villages are labeled with the first n letters of the alphabet, capitalized. Each data set is completed with n-1 lines that start with village labels in alphabetical order. There is no line for the last village. Each line for a village starts with the village label followed by a number, k, of roads from this village to villages with labels later in the alphabet. If k is greater than 0, the line continues with data for each of the k roads. The data for each road is the village label for the other end of the road followed by the monthly maintenance cost in aacms for the road. Maintenance costs will be positive integers less than 100. All data fields in the row are separated by single blanks. The road network will always allow travel between all the villages. The network will never have more than 75 roads. No village will have more than 15 roads going to other villages (before or after in the alphabet). In the sample input below, the first data set goes with the map above.
The output is one integer per line for each data set: the minimum cost in aacms per month to maintain a road system that connect all the villages. Caution: A brute force solution that examines every possible set of roads will not finish within the one minute time limit.
样例输入
9
A 2 B 12 I 25
B 3 C 10 H 40 I 8
C 2 D 18 G 55
D 1 E 44
E 2 F 60 G 38
F 0
G 1 H 35
H 1 I 35
3
A 2 B 10 C 40
B 1 C 20
0
样例输出
216
30
时间限制
Time Limit: 2000/1000 MS (Java/Others)
空间限制
65536/32768 K (Java/Others)
Kruskal算法
// 克鲁斯卡尔算法
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 图的最大顶点数不会超过 N
const int N = 10000;
// 保存从x节点到y节点的权重l
struct Edge
{
int x, y;
int l;
};
// 进行并查集运算得数组
int f[N];
vector<Edge> edges;
int find(int x)
{
return x == f[x] ? x : (f[x] = find(f[x]));
}
void init_f(int n)
{
for (int i = 0; i <= n; i++)
f[i] = i;
}
bool cmp(Edge a, Edge b)
{
return a.l < b.l;
}
int main()
{
int n;
while (cin >> n, n)
{
edges.clear();
init_f(9999);
char x, y;
int m;
for (int i = 0; i < n - 1; i++)
{
cin >> x >> m;
for (int j = 0, l; j < m; j++)
{
cin >> y >> l;
edges.push_back({ x - 'A' + 1, y - 'A' + 1, l });
}
}
sort(edges.begin(), edges.end(), cmp);
int res = 0;
for (int i = 0; i < edges.size(); i++)
{
int x = edges[i].x;
int y = edges[i].y;
x = find(x), y = find(y);
if (x == y)
continue;
res += edges[i].l;
f[x] = y;
}
cout << res << endl;
}
}
Prim算法
// Prim算法
#include <iostream>
#include <queue>
#include <cstring>
#include <vector>
using namespace std;
// 保存与V相连的所有的边
struct Edge
{
int to;
int l;
friend bool operator<(Edge a, Edge b)
{
return a.l > b.l;
}
};
const int N = 100;
vector< vector<Edge> > map(N);
int Prim()
{
priority_queue<Edge> que;
int vis[N];
memset(vis, 0, sizeof(vis));
vis[1] = 1;
for (int i = 0; i < map[1].size(); i++)
que.push(map[1][i]);
int res = 0;
while (que.size())
{
Edge edge = que.top();
que.pop();
if (vis[edge.to])
continue;
vis[edge.to] = 1;
res += edge.l;
for (int i = 0; i < map[edge.to].size(); i++)
que.push(map[edge.to][i]);
}
return res;
}
int main()
{
int n;
while (cin >> n, n)
{
for (int i = 0; i < N; i++)
map[i].clear();
char x, y;
int m;
for (int i = 0; i < n - 1; i++)
{
cin >> x >> m;
for (int j = 0, l; j < m; j++)
{
cin >> y >> l;
// 无向图保存,一开始就忘了,调bug调了一会
map[x - 'A' + 1].push_back({ y - 'A' + 1, l });
map[y - 'A' + 1].push_back({ x - 'A' + 1, l });
}
}
cout << Prim() << endl;
}
}