题目提交链接:http://lx.lanqiao.cn/problem.page?gpid=T114
问题描述
C国由n个小岛组成,为了方便小岛之间联络,C国在小岛间建立了m座大桥,每座大桥连接两座小岛。两个小岛间可能存在多座桥连接。然而,由于海水冲刷,有一些大桥面临着不能使用的危险。
如果两个小岛间的所有大桥都不能使用,则这两座小岛就不能直接到达了。然而,只要这两座小岛的居民能通过其他的桥或者其他的小岛互相到达,他们就会安然无事。但是,如果前一天两个小岛之间还有方法可以到达,后一天却不能到达了,居民们就会一起抗议。
现在C国的国王已经知道了每座桥能使用的天数,超过这个天数就不能使用了。现在他想知道居民们会有多少天进行抗议。输入格式
输入的第一行包含两个整数n, m,分别表示小岛的个数和桥的数量。
接下来m行,每行三个整数a, b, t,分别表示该座桥连接a号和b号两个小岛,能使用t天。小岛的编号从1开始递增。输出格式
输出一个整数,表示居民们会抗议的天数。
样例输入
4 4
1 2 2
1 3 2
2 3 1
3 4 3样例输出
2
样例说明
第一天后2和3之间的桥不能使用,不影响。
第二天后1和2之间,以及1和3之间的桥不能使用,居民们会抗议。
第三天后3和4之间的桥不能使用,居民们会抗议。数据规模和约定
对于30%的数据,1<=n<=20,1<=m<=100;
对于50%的数据,1<=n<=500,1<=m<=10000;
对于100%的数据,1<=n<=10000,1<=m<=100000,1<=a, b<=n, 1<=t<=100000。
思路
逆向思维的并查集(并查集介绍见代码后知识点部分链接)
①先把每个小岛的上级设置成自己,并用vector类对象s保存输入的数据,
②把数据按使用天数从大到小进行排序(排序方法这里用来结构体中符号重载,当然也可以通过定义sort()函数第3个变量cmp来实现)
③排好序的数据依次进行判断(桥两头的岛去掉当前的桥是否相连接,不相连抗议天数++)(这里涉及并查集关键部分,具体做法见并查集介绍)
④这时就可以让这两个岛连接起来了,并把其使用天数放到set类对象sset中,记录不重复的天数
⑤最后输出sset.size(),即为所求。
代码
学习参考后所写
#include <iostream>
#include <algorithm>
#include <vector>
#include <set>
using namespace std;
const int maxn=1e4+10;
struct node{
int x,y,t;//桥两头的岛x和y 能使用的天数
int operator < (node b)//node结构体"<"重载
{
return t > b.t;//以能使用的天数从高到低进行排序
}
};
int n,m; //小岛数 能使用的天数
int pre[maxn]; //用于存放小岛的上级小岛
vector <node > s;//用于桥的基本信息 两端岛名 使用天数
set <int > sset;//set类简单运用 用于排除重复元素
int findx(int x)//寻找岛x的最高上级 和 压缩路径
{
int r=x;
while(pre[r]!=r)//寻找岛x的最高上级
r=pre[r];
while(pre[x]!=r)//此时的r代表岛x的最高上级
{//把找寻过程中的所有小岛上级变成最高上级r
int t=pre[x];
pre[x]=r;
x=t;
}
return r;//返回岛x的最高上级
}
int main()
{
cin>>n>>m;
for(int i=0;i<n;i++)
pre[i]=i;//此时所有小岛的上级小岛都是他们自己
for(int i=0;i<m;i++)
{
node mid;
cin>>mid.x>>mid.y>>mid.t;
s.push_back(mid);
}
sort(s.begin(),s.end());//按使用天数从高到低排序
int ans=0; //岛民抗议天数
//由使用天数高到低进行判断,巧妙地把拆桥问题变成了建桥判断
for(int i=0;i<m;i++)
{
if(findx(s[i].x)!=findx(s[i].y))
//不相等意味着,岛x和岛y在当前桥无法使用后互不连通
{
pre[findx(s[i].y)]=findx(s[i].x);//建桥(本题思路是倒着来的)
sset.insert(s[i].t);
}
}
cout<<sset.size()<<endl;//最后输出sset中存放了多少元素即为答案(set类中元素不重复)
return 0;
}
知识点
1.并查集
经典并查集讲解链接:https://blog.csdn.net/niushuai666/article/details/6662911
并查集经典入门习题杭电上的畅通工程:http://acm.hdu.edu.cn/showproblem.php?pid=1232
畅通工程代码参考
#include<iostream>
#include<cstdio>
using namespace std;
int pre[1010];
int unionsearch(int root)
{
int son,tmp;
son=root;
while(root!=pre[root])//寻找当前城市所在城市群的中心城市
root=pre[root];
while(son!=root)//路径压缩
{//将寻找中心城市过程中遇到的城市的上级都设置成中心城市
tmp=pre[son];
pre[son]=root;
son=tmp;
}
return root;
}
int main()
{
int num,road; //城市数目 已有的道路数
while((cin>>num>>road)&&num)
{
int total=num-1;//为了联通需要修的路的数目(此时还一条没修)
for(int i=1;i<=num;i++)//每个城市先自成一个城市群
pre[i]=i;
while(road--)
{
int start,end;
cin>>start>>end;
//寻找当前路连接的两个城市他们的城市群中心城市
int root1=unionsearch(start);
int root2=unionsearch(end);
if(root1!=root2)//中心城市不一样
{
pre[root1]=root2;
//任意修改一个城市群中心城市的中心城市变成另一个城市群的中心城市
total--; //需要修路的数目减一
}
}
cout<<total<<endl;
}
}
2.set类
感觉简单掌握即可,主要用来去重用处理
知识点参考链接:https://www.cnblogs.com/zyxStar/p/4542835.html
3.提醒下,sort()函数结构体很多时候会这么写,
bool operator < (const node &r)const
{
return t>r.t;
}