P1111 修复公路 题解

这道题就是并查集的练手好题QAQ

如果对并查集不熟悉的同学可以做一下这道题

来看一下题目:

给出A地区的村庄数N,和公路数M,公路是双向的。并告诉你每条公路的连着哪两个村庄,并告诉你什么时候能修完这条公路。问最早什么时候任意两个村庄能够通车,即最早什么时候任意两条村庄都存在至少一条修复完成的道路(可以由多条公路连成一条道路)

我们可以将这N个村庄看成N个点,其中M是我们要合并的次数。

很明显,修路时间t越早的就要越早合并,所以要先排序!(样例中都能看出来)

排序后,按照时间逐个合并,如果合并过程中道路连通,那么输出时间;否则输出-1

但是如果按时间排序,x,y也要跟着动,怎么办呢?用结构体!结构体不会的可以自行离开了

如何判断当前N个点有没有在一个集合内呢?

我们设fa[i]是第i个人的祖先,初始化fa[i]=i,各自是各自的祖先

当合并一次之后,我们扫一遍fa数组,统计fa[i]=i的个数:

  1. 如果只有1个fa[i]=i,就说明所有的全部联通了(因为只有1个人是自己的祖先,别的人都跟着他)
  2. 如果不止1个,就说明当前没有全部联通(因为两个人互相没有关系),需要继续合并。

时间复杂度可能会有点高,但足够通过本题了。

上代码:

#include<bits/stdc++.h>
using namespace std;
int fa[1000+10],n,m;
struct node
{
 int x,y,t;
}a[100000+10];//结构体大法好!
bool cmp(node fir,node sec)
{
 return fir.t<sec.t;
}//按照时间排序
int gf(int x)
{
 if(fa[x]==x) return x;
 return fa[x]=gf(fa[x]);
    //这句是路径压缩,前面的题解已经说过,此处不再阐述
}
void hb(int x,int y)
{
 int fx=gf(x);//找到x的祖先
 int fy=gf(y);//找到y的祖先
 fa[fx]=fy;//让fx认fy为祖先
}//合并操作
bool check()
{
 int sum=0;
 for(int i=1;i<=n;i++)
 {
  if(fa[i]==i) sum++;//统计独立集合的个数
  if(sum==2) return 0;//发现有两个就返回false应该会省一点时间
 }
 return 1;//只有1个集合,返回true
}//判断集合个数
int main()
{
 scanf("%d%d",&n,&m);
 for(int i=1;i<=n;i++) fa[i]=i;//初始化
 for(int i=1;i<=m;i++) scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].t);
 sort(a+1,a+m+1,cmp);//按时间排序
 for(int i=1;i<=m;i++)
 {
  hb(a[i].x,a[i].y);//进行合并
  if(check())//如果只有1个集合
  {
   printf("%d\n",a[i].t);//输出时间
   return 0;//愉快的结束主程序
  }
 }
 printf("-1\n");//所有公路修完了仍没有联通(集合个数>=2),输出-1
 return 0;//愉快的结束主程序
}
posted @ 2022-04-12 21:27  Plozia  阅读(50)  评论(0编辑  收藏  举报