• 并查集经典例题分析


    http://acm.hdu.edu.cn/showproblem.php?pid=1232
     
    1.某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇。省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要互相间接通过道路可达即可)。问最少还需要建设多少条道路?
     
    Input

    测试输入包含若干测试用例。每个测试用例的第1行给出两个正整数,分别是城镇数目N ( < 1000 )和道路数目M;随后的M行对应M条道路,每行给出一对正整数,分别是该条道路直接连通的两个城镇的编号。为简单起见,城镇从1到N编号。
    注意:两个城市之间可以有多条道路相通,也就是说
    3 3
    1 2
    1 2
    2 1
    这种输入也是合法的
    当N为0时,输入结束,该用例不被处理。

    Sample Input
    4 2
    1 3
    4 3
    3 3
    1 2
    1 3
    2 3
    5 2
    1 2
    3 5
    999 0
    0
     
    Sample Output
    1
    0
    2
    998

    题解:

      由题意可知,对任意一个"树杈"上的子节点,都可以认为它们是可达的,从这个角度看来,它们是属于一个集合的元素,这样可以通过并查集的思想来实现,方便且快捷!

    把相通的两个地方给连接在一起,这样的话就相当于把两个地方所在集合合并,即合并后的集合中的元素是互通的.   那么最终只需要得出一共有多少个集合,比如最终有32个集合

    那么,32-1就是最少需要添加的道路数 !

    代码:

      

     1 #include<iostream>
     2 using namespace std;
     3 int par[1001];
     4 int rk[1001]={0};
     5 int findroot(int x)
     6 {
     7     if(par[x]==-1) return x;
     8     else return findroot(par[x]);
     9 }
    10 
    11 void unite(int x,int y)
    12 {
    13     int xroot=findroot(x),yroot=findroot(y);
    14     if(xroot!=yroot)
    15     {
    16         if(rk[xroot]<rk[yroot])
    17         {
    18             par[x]=y;
    19         }
    20         else if(rk[xroot]>rk[yroot])
    21         {
    22             par[y]=x;
    23         }
    24         else
    25         {
    26             par[y]=x;
    27             rk[x]++;
    28         }
    29     }
    30 }
    31 void init(int n)
    32 {
    33     for(int i=1;i<=n;i++)
    34     {
    35         par[i]=-1;
    36         rk[i]=0;
    37     }
    38     return ;
    39 }
    40 bool same(int x,int y)
    41 {
    42     return findroot(x)==findroot(y);
    43 }
    44 int main()
    45 {
    46     int n,m,count,x,y;
    47     while(1)
    48     {
    49         cin>>n>>m;
    50         if(n==0) return 0;
    51         init(n);
    52         count=n;
    53         for(int i=0;i<m;i++)
    54         {
    55             cin>>x>>y;
    56             if(x==0) return 0;
    57             //cin>>y;
    58             if(findroot(x)!=findroot(y)) count--;//唯有不同源,即确有集合合并才累积 
    59             unite(x,y);
    60             //count--;
    61         }
    62         cout<<count-1<<endl;
    63         
    64     }
    65     return 0;
    66 }
    67 /*
    68 int find(int x)
    69 {
    70     if(par[x]==-1) return x;
    71     else return find(par[x]); 
    72 } 
    73 
    74 void unite(int x,int y)
    75 {//简单可行,这里并没有按秩合并,所以同常形成的树比较乱,比较高 
    76     x=find(x);
    77     par[x]=y;
    78 }

      题干中的重点是,城镇的互通,只要互通就可以看作在一个集合.    集合之间随意两个节点的连线就使得两个集合中所有城镇互通,这显然又组成了一个更大的集合.

  • 相关阅读:
    go语言中通过http访问需要认证的api
    Mysql两个time类型计算时间相减
    gorm中数据库datetime类型的映射和time.Time的格式化
    最详细的六种装饰器写法,学不会你找我!
    深度学习中的四种激活函数
    看完这篇文章,相信我,你已经掌握正则表达式了!
    新手还在问学Python应该看什么书,老手已经进来下载了(附100本pdf电子书下载)
    Python轻松实现一个毕业生信息管理系统!
    Python一键搞定批量合成PDF
    网易云10万+音乐竟然能用Python一键下载!
  • 原文地址:https://www.cnblogs.com/zww-kjj/p/12245663.html
Copyright © 2020-2023  润新知