Prim算法 求最小生成树的一种算法
最小生成树(MST):
给定一个带权的无向连通图,要求得到一颗生成树,使得树上所有边的权值相加和最小
Prim算法:
以点为中心
- 先选定一个顶点作为生成树的起始点,并且将该顶点标记为已访问过
- 然后用这颗生成树中已有的顶点分别去寻找与它相邻且未被它访问过的顶点,记录下各自的权值,选择其中带有最小权值的顶点,并把它标记为已访问过
- 重复step2操作,直到所有的顶点均被访问,此时有n-1条边
Prim算法案例
T1
题目描述
某国有n个城市,它们互相之间没有公路相通,因此交通十分不便。为解决这一“行路难”的问题,政府决定修建公路。修建公路的任务由各城市共同完成。
修建工程分若干轮完成。在每一轮中,每个城市选择一个与它最近的城市,申请修建通往该城市的公路。政府负责审批这些申请以决定是否同意修建。
政府审批的规则如下:
(1)如果两个或以上城市申请修建同一条公路,则让它们共同修建;
(2)如果三个或以上的城市申请修建的公路成环。如下图,A申请修建公路B,B申请修建公路C,C申请修建公路A。则政府将否决其中最短的一条公路的修建申请;
(3)其他情况的申请一律同意。
一轮修建结束后,可能会有若干城市可以通过公路直接或间接相连。这些可以互相:连通的城市即组成“城市联盟”。在下一轮修建中,每个“城市联盟”将被看作一个城市,发挥一个城市的作用。
当所有城市被组合成一个“城市联盟”时,修建工程也就完成了。
你的任务是根据城市的分布和前面讲到的规则,计算出将要修建的公路总长度。
输入格式
第一行一个整数n,表示城市的数量。(n≤5000)
以下n行,每行两个整数x和y,表示一个城市的坐标。(-1000000≤x,y≤1000000)
输出格式
一个实数,四舍五入保留两位小数,表示公路总长。(保证有惟一解)
输入输出样例
4
0 0
1 2
-1 2
0 4
6.47
核心代码:
void prim() {//p[]表示距离 vis[i]表示此顶点已访问过
p[1] = 0;//已经在这颗生成树上
for (int i = 0; i < n - 1; i++) {//n-1条边,n-1次循环
int rec = 0;
for (int j = 1; j <= n; j++) {
if (!vis[j] && (rec==0||p[j] < p[rec])) {
rec = j;//更新rec
}
}
vis[rec] = true;
for (int j = 1; j <= n; j++) {
if (!vis[j]) {
p[j] = min(p[j], getDis(point[j], point[rec]));
}
}
}
}
T2
4
0 4 9 21
4 0 8 17
9 8 0 16
21 17 16 0
28
OJ
很明显,该题是一道标准的用Prim求最小生成树的问题
AC code
#include<bits/stdc++.h>
using namespace std;
const int MAX = 1e8;
int n, len[105][105],res;
bool vis[105];
void prim();
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cin >> len[i][j];
}
}
prim();
cout << res << endl;
return 0;
}
void prim() {
vis[1] = true;
int tmpi = 0, tmpj = 0;
for (int lp = 0; lp < n - 1; lp++) {
int rec = MAX;
for (int i = 1; i <= n; i++) {
if (vis[i]) {
for (int j = 1; j <= n; j++) {
if (!vis[j] && rec > len[i][j]) {
rec = len[i][j];
tmpi = i; tmpj = j;
}
}
}
}
res += rec;
vis[tmpj] = true;
}
}