蓝桥杯训练题1435: [蓝桥杯][历届试题]国王的烦恼【用到“并查集”和卡鲁斯卡尔算法找最小生成树】

题目链接

https://www.dotcpp.com/oj/problem1435.html

题目描述:

在这里插入图片描述在这里插入图片描述

思路:

我们可以看到上面的题述,其中抗议主要说的是“如果前一天两个小岛之间还有方法可以到达,后一天却不能到达了,居民们就会一起抗议”,就是两小岛之前有桥是通的,而后一天就倒塌了,这样就会抗议,那么我们可以反过来想:“如果在第x天建好了这个桥,那么在这第x天的前x-1天都不会抗议。”说起来有点抽象,大家请看这个题目数据:

4 4
1 2 2
1 3 2
2 3 1
3 4 3

我们模拟下这个过程:

天数1234
桥2->3有效失效失效失效
桥1->2有效有效失效失效
桥1->3有效有效失效失效
桥3->4有效有效有效失效
是否抗议?否, 桥都存在 否, 2->3桥虽然失效, 但是2->1->3仍然存在通道 是, 岛2彻底没有通道到岛3 是, 岛3彻底没有桥到岛4
然后我们反过来思考的话,表格是这样的:
天数4321
桥2->3不存在不存在不存在建成
桥1->2不存在不存在建成有效
桥1->3不存在不存在建成有效
桥3->4不存在建成有效有效
是否抗议?否, 没有一座桥在建 是, 桥3->4在建设, 且岛3和4之前没有通道 是, 建设1->2的桥和1->3的桥 之前岛1和2以及岛1和3没有通道 否, 虽然在建设2->3的桥,但是之前已经存在2到3的路径了即,2->3->1

相信大家观看下面的那个表格就会想到,这个其实就是在找最小生成树嘛!
对的,就是找最小生成树。
然后我们说下找最小生成树,一般来说找最小生成树有:普利姆算法和卡鲁斯卡尔算法,普利姆算法讲究从点开找,而卡鲁斯卡尔讲究从边开找。因为我们这题主要是桥即图里面的边,所以我采用的就是卡鲁斯卡尔算法。

然后要用到这个算法又需要用到并查集:传送门
我们并查集在代码中用到的方法如下:

int f[N];
int high[N];

void init(){
	for(int i = 1;i <= n;i++){
		f[i] = i;
	}
}

int getFather(int a){
	if(f[a] == a){
		return a;
	}
	return f[a] = getFather(f[a]);
}

void merge(int a,int b){
	int t1 = getFather(a);
	int t2 = getFather(b);
	if(t1 != t2){
		if(high[t1] > high[t2]){
			f[t2] = t1;
		}else{
			f[t1] = t2;
			if(high[t1]==high[t2]){
				high[t2]++;
			}
		}
	}		
}

ac代码:

#include <bits/stdc++.h>

using namespace std;

#define N 10005
#define M 100005

struct route{
	int a;
	int b;
	int value;
}bridge[M];
int n,m;
int f[N];
int high[N];

void init(){
	for(int i = 1;i <= n;i++){
		f[i] = i;
	}
}

int getFather(int a){
	if(f[a] == a){
		return a;
	}
	return f[a] = getFather(f[a]);
}

void merge(int a,int b){
	int t1 = getFather(a);
	int t2 = getFather(b);
	if(t1 != t2){
		if(high[t1] > high[t2]){
			f[t2] = t1;
		}else{
			f[t1] = t2;
			if(high[t1]==high[t2]){
				high[t2]++;
			}
		}
	}		
}

bool cmp(struct route x,struct route y){//按照建桥天数降序排序 
	return x.value>y.value;
}

int main(){
	int ans;//记录抗议天数 
	cin>>n>>m;
	
	for(int i = 1;i <= m;i++){
		cin>>bridge[i].a>>bridge[i].b>>bridge[i].value;
	}
	
	init();
	sort(bridge+1,bridge+m+1,cmp);
	int temp;
	ans = 0;
	temp = M;
	for(int j = 1;j <= m;j++){
		if(getFather(bridge[j].a)!=getFather(bridge[j].b)){
			merge(bridge[j].a,bridge[j].b);
			if(bridge[j].value < temp){
				ans++;
				temp = bridge[j].value;
			}
		}
		if(ans == n-1){//如果已经找到了n-1个桥了,证明已经找到了最小生成树了,再往后居民不会再抗议了。 
			break;
		}
	}
	cout<<ans<<endl;
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值