Prim普里姆
这种算法有些类似dijkstra,每次取出最近的一个节点拓展出去,直到所有节点都在一个连通块中为止
同样的,prim算法也可以使用堆优化。这和dijkstra是一个道理
例题
Description
八中草坪上有N个水龙头,位于(xi,yi)
求将n个水龙头连通的最小费用。
任意两个水龙头可以修剪水管,费用为欧几里得距离的平方。
校长只愿意修费用大于等于c的水管。
Input
第一行给出N,C
接下来N行给出点的坐标x,y
1 <= N <= 2000
0 <= xi, yi <= 1000
1 <= C <= 1,000,000
Output
输出最小费用,如果无解输出-1
Sample Input
3 11 0 2 5 0 4 3
Sample Output
46
#include<bits/stdc++.h>
using namespace std;
int n, c;
int x[2010], y[2010];
bool vis[2010];
int d[2010];
int ans;
int cnt;
inline int read() {
int x = 0, f = 1; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();}
return x * f;
}
struct node {
int f, s;
node () {}
node (int ff, int ss) {
f = ff, s = ss;
}
bool operator < (const node & x) const {
return x.s < s;
}
};
void prim() {
priority_queue<node> q;
memset(d, 0x3f3f3f3f, sizeof d);
d[1] = 0;
q.push(node(1, 0));
while (!q.empty()) {
int f = q.top().f, s = q.top().s;
q.pop();
if (vis[f]) continue;
vis[f] = true;
ans += s;
cnt++;
for (int i = 1; i <= n; i++) {
if (vis[i]) continue;
int w = (x[i] - x[f]) * (x[i] - x[f]) + (y[i] - y[f]) * (y[i] - y[f]);
if (w < c) continue;
if (d[i] > w) {
d[i] = w;
q.push(node(i, d[i]));
}
}
}
}
int main() {
n = read(), c = read();
for (int i = 1; i <= n; i++) x[i] = read(), y[i] = read();
prim();
if (cnt == n) printf("%d", ans);
else puts("-1");
}
Kruskal克鲁斯卡尔
kruskal算法利用并查集
首先将所有边按边权升序排序,然后从最小边开始插入。
如果当前插入的这条边连接的两个节点已经联通,那么跳过当前边
直到插入n - 1条边为止。
代码
#include<bits/stdc++.h>
using namespace std;
struct edge {
int u, v, w;
} e[2000010];
int n, m;
int fa[5010];
int sum;
inline int read() {
int x = 0, f = 1; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();}
return x * f;
}
int find(int x) {
if (x == fa[x]) return x;
return fa[x] = find(fa[x]);
}
bool cmp(edge a, edge b) {
return a.w < b.w;
}
void kruskal() {
int cnt = 0;
for (int i = 1; i <= m; i++) {
int eu = find(e[i].u), ev = find(e[i].v);
if (eu == ev) continue;
sum += e[i].w;
fa[ev] = eu;
if (++cnt == n - 1) break;
}
}
int main() {
n = read(), m = read();
for (int i = 1; i <= n; i++) fa[i] = i;
for (int i = 1; i <= m; i++) {
e[i].u = read(), e[i].v = read(), e[i].w = read();
}
sort(e + 1, e + n + 1, cmp);
kruskal();
printf("%d", sum);
return 0;
}
两种算法优劣对比
通过观察不难发现,kruskal算法需要将所有的边都存下来进行排序,所以,当边的数量过多的时候,kruskal就不如prim
而prim算法虽然能处理边数量较多的情况,但当边的数量较少时,它的效率是不如kruskal算法的。
简单来说就是,在稀疏图中kruskal优于prim;而在稠密图中,prim优于kruskal
最小生成树的拓展
度限制最小生成树
次小生成树
……正在施工
关于最小生成树的拓展将继续补充