题目链接
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
我们模拟下这个过程:
天数 | 1 | 2 | 3 | 4 |
桥2->3 | 有效 | 失效 | 失效 | 失效 |
桥1->2 | 有效 | 有效 | 失效 | 失效 |
桥1->3 | 有效 | 有效 | 失效 | 失效 |
桥3->4 | 有效 | 有效 | 有效 | 失效 |
是否抗议? | 否, 桥都存在 | 否, 2->3桥虽然失效, 但是2->1->3仍然存在通道 | 是, 岛2彻底没有通道到岛3 | 是, 岛3彻底没有桥到岛4 |
天数 | 4 | 3 | 2 | 1 |
桥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;
}