最小生成树

最小生成树

我们定义无向连通图的最小生成树(MST)为边权和最小的生成树。

【 最小生成树可以用来解决用最小的“代价”用N-1条边连接N个点的问题。】 例题:

[USACO3.1]最短网络 Agri-Net

John 被选为他们镇的镇长!他其中一个竞选承诺就是在镇上建立起互联网,并连接到所有的农场。当然,他需要你的帮助。FJ 已经给他的农场安排了一条高速的网络线路,他想把这条线路共享给其他农场。为了用最小的消费,他想铺设最短的光纤去连接所有的农场。你将得到一份各农场之间连接费用的列表,你必须找出能连接所有农场并所用光纤最短的方案。每两个农场间的距离不会超过 105
第一行农场的个数 N(3 ≤ N ≤ 100)。
接下来是一个 N X N 的矩阵,表示每个农场之间的距离。理论上,他们是 N 行,每行由 N 个用空格分隔的数组成,实际上,由于每行 80 个字符的限制,因此,某些行会紧接着另一些行。当然,对角线将会是 0,因为不会有线路从第 i 个农场到它本身。
只有一个输出,其中包含连接到每个农场的光纤的最小长度。

Input
4
0 4 9 21
4 0 8 17
9 8 0 16
21 17 16 0
Output
28

我们来介绍两种求解最小生成树的算法,Kruskal算法Prim算法


Kruskal

前置知识:并查集、贪心

1.【原理】

我们先认为图上的每一个点都是单个的点,(并查集思路,即fa[i] = i;

然后将每个点通过给出的边连接为一颗树,通过贪心的思想,要先将最小的边加入到M S T中(排序,即sort(e + 1, e + 1 + m, cmp);,对于加边操作,我们使用并查集来维护,fa[fat(e[i].u)] = fat(e[i].v);

所以利用贪心的思想,将权值按照从小到大排序,然后将小边加入到M S T中,为了防止形成环,要在选边的过程中利用并查集进行判断是否已经连边,就是Kruskal算法的大体过程。

2.【图示】咕咕咕~~~

3.【特别地】Kruskal的时间复杂度为 \(O(m\log n)\),因此有利于稀疏图。

4.【代码实现】

void Kruskal() {
	for (int i = 1; i <= n; ++ i) fa[i] = i;  //并查集 预处理
	sort(e + 1, e + 1 + m, cmp);              //按照 边权大小 进行排序
	for (int i = 1; i <= m; ++ i) {
		int fat_u = fat(e[i].u);
		int fat_v = fat(e[i].v);
		if (fat_u == fat_v) continue;     //判断是否 已经连边
		fa[fat_u] = fat_v;                //加边操作
		ans += e[i].w;                    //记录边权和
                //………………
		tot ++ ;
		if (tot == n - 1) break;          //树的边数为点数-1 因此跳出
	}
}

Prim

1.【原理】

与Dijkstra和SPFA的“蓝白点”的思路是相似的,白点代表已经进入最小生成树的点,蓝点代表未进入最小生成树的点。

2.【图示】咕咕咕~~~

3.【特别地】有利于稠密图。

4.【代码实现】

void Prim() {
    for (int i = 1; i <= n; ++ i) {
	int k = 0
	for (int j = 1; j <= n; ++ j)        //找一个与白点相连的权值最小的蓝点k
	    if(!vis[j] && mp[j] < mp[k])
		k = j;
	vis[k] = true;                       //蓝点k加入生成树,标记为白点
	for (int j = 1; j <= n; ++ j)
	    if(!vis[j] && sm[k][j] < dis[j])
		dis[j] = sm[k][j];
	}
    for (int i = 1; i <= n; ++ i)            //累加权值 
	ans += dis[i];
}
void Prim() {
    priority_queue <PAIR> q;
    int ans = 0;
    dis[1] = 0;
    q.push(make_pair(dis[1],1));
    while (!q.empty()){
        int u = q.top().second, d = q.top().first;
        q.pop();
        if (vis[u]) continue;
        vis[u] = true;
        ans += d;
        for (int i = h[u]; i; i = e[i].nxt) {
        	int v = sm[i].v;
        	if (dis[v] > sm[i].w) {
        		dis[v] = sm[i].w;
        		 q.push(make_pair(dis[v],v));
		}
	}
    }
}

posted @ 2022-09-24 17:54  Ciaxin  阅读(49)  评论(0编辑  收藏  举报