最小生成树算法

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

 

最小生成树的拓展

度限制最小生成树

参考博文

参考论文

次小生成树

……正在施工

关于最小生成树的拓展将继续补充

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值