HDU 1233 还是畅通工程(最小生成树, Prim+优先队列 || Kruskal+并查集)

链接 - http://acm.hdu.edu.cn/showproblem.php?pid=1233

分析

  • 生成树:给定一个边带权的无向图,由全部n个顶点和n-1条边构成的无向连通图称为原图的一棵生成树
  • 最小生成树:边的权值之和最小的生成树被称为最小生成树;图的最小生成树不一定唯一
  • 定理1:任意一棵最小生成树一定包含无向图中权值最小的边
  • 定理2:对于任意一个顶点V,一定存在包含V的最小权值边的最小生成树
    证明:设最小生成树中的某个顶点V有k条边,且这k条边不含顶点V的权值最小边emin,当k>0时:
    1)有t1、t2、……、tk共k棵互不连通的树与顶点V相连
    2)顶点V的权值最小边的另一个顶点必然属于ti,1 <= i <= k。
    3)将V与ti的连通边用emin替代后,还是生成树,且权值和不大于原有生成树,故还是最小生成树。
  • 定理3:按定理2遍历顶点,得到生成森林。对于森林中的任意1棵树,一定存在最小生成树包含这棵树与其它树间的最小权值边。
    证明:将森林中的每一棵树看作一个顶点,则转化为定理2的情形。

算法

  • Kruskal: 以最小权值边为基础逐步合并次小的权值边,从而将森林合并成一棵树
  • Prim:以任意点为基础逐步合并其它点,合并规则是未合并的点到已合并的点有最小权值边

代码

Kruskal【并查集】

/* hdu 1233 还是畅通工程 */
#include<bits/stdc++.h>
using namespace std;
#define MXN 110
#define MXM 110*55
int N, fa[MXN], ans;
struct Town{
	int a, b, c;
	bool operator<(Town t){ return c < t.c; }
}t[MXM];
int find(int r){
	if(r != fa[r]) fa[r] = find(fa[r]);
	return fa[r];
}
void merge(Town &t){
	int t1 = find(t.a);
	int t2 = find(t.b);
	if(t1 != t2){
		fa[t1] = t2;
		ans += t.c;
	}
}
int main(){
	int M;
	while(scanf("%d", &N) == 1){
		if(N == 0) break;
		M = N*(N-1)/2;
		for(int i = 1; i <= M; i++)
			scanf("%d %d %d", &t[i].a, &t[i].b, &t[i].c);
		sort(t+1, t+M+1);
		for(int i = 1; i <= N; i++) fa[i] = i;
		ans = 0;
		for(int i = 1; i <= M; i++) merge(t[i]);
		
		printf("%d\n", ans);
	}
	return 0;
}

Prim【优先队列】

/* hdu 1233 还是畅通工程 */
#include<bits/stdc++.h>
using namespace std;
#define MXN 110
#define MXM 110*55
int N, M, vis[MXN], ans;
vector<int> mp[MXN];
struct Road{
	int a, b, c;
	bool operator<(Road r){ return c < r.c; }
}rs[MXM];
struct cmp{
	bool operator()(int x, int y){
		return rs[y] < rs[x];
	}
};
priority_queue<int, vector<int>, cmp> q;
int main(){
	while(scanf("%d", &N), N){
		M = N*(N-1)/2;
		for(int i = 1; i <= N; i++) mp[i].clear();
		for(int i = 1; i <= N; i++) vis[i] = 0;
		while(q.size()) q.pop();
		for(int i = 1; i <= M; i++){
			scanf("%d %d %d", &rs[i].a, &rs[i].b, &rs[i].c);
			mp[rs[i].a].push_back(i);
			mp[rs[i].b].push_back(i);
		}		
		vis[1] = 1, ans = 0;
		for(int i = 0; i < mp[1].size(); i++)
			q.push(mp[1][i]);
			int r, v;
		while(q.size()){
			r = q.top(), q.pop();
			if(vis[rs[r].a] == 0) v = rs[r].a;
			else if(vis[rs[r].b] == 0) v = rs[r].b;
			else continue;
			ans += rs[r].c, vis[v] = 1;
			for(int i = 0; i < mp[v].size(); i++){
				if(vis[rs[mp[v][i]].a] == 0 || vis[rs[mp[v][i]].b] == 0)
					q.push(mp[v][i]);
			}	
		}
		printf("%d\n", ans);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jpphy0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值