三道题讲并查集

来源:互联网 发布:天庭淘宝店txt 编辑:程序博客网 时间:2024/06/07 21:55

HDU1232--畅通工程

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

Input
测试输入包含若干测试用例。每个测试用例的第1行给出两个正整数,分别是城镇数目N ( < 1000 )和道路数目M;随后的M行对应M条道路,每行给出一对正整数,分别是该条道路直接连通的两个城镇的编号。为简单起见,城镇从1到N编号。  注意:两个城市之间可以有多条道路相通,也就是说
3 3
1 2
1 2
2 1
这种输入也是合法的
当N为0时,输入结束,该用例不被处理。
Output
对每个测试用例,在1行里输出最少还需要建设的道路数目。
Sample Input
4 21 34 33 31 21 32 35 21 23 5999 00
Sample Output
1
2
998

所谓并查集呢,就是用一个数组,把一个元素的父节点存起来。父节点是什么呢,一般就是根据题目来说对当前点有指向关系的节点。
比如在这道题中,如果节点2和节点1相连,那么可以设节点1为节点2的父节点(反之也行)。那么当节点3和节点2相连时,并查集的作用就显现出来了,
节点3会与节点1相连。
并查集的思想是,若是两个节点相连,那么先找到他们的根节点,然后根节点相连。一个元素最初的根节点是自身。
如图

AC代码

#include <iostream>#include <cstring>#include <cstdio>using namespace std;int asd[1008];//指向父节点int get(int a){    int b=a;    while(asd[b]!=b)//寻找父节点    {        b=asd[b];    }    return b;}void conbin(int a,int b)//建立父节点联系{    int a1=get(a);    int b1=get(b);    if(a1!=b1)        asd[a1]=b1;}int main(){    int n,m,x,y,num,i;    while(scanf("%d",&n)&&n){        for(i=0;i<=n;i++)            asd[i]=i;        scanf("%d",&m);        for(int j=m;j>0;j--){            scanf("%d%d",&x,&y);            conbin(x,y);        }        num=-1;        for(i=1;i<=n;i++)            if(asd[i]==i)                num++;        cout<<num<<endl;    }    return 0;}


那么再看一些模糊点的题目。。


HUD--3172  Virtual Friends

These days, you can do all sorts of things online. For example, you can use various websites to make virtual friends. For some people, growing their social network (their friends, their friends' friends, their friends' friends' friends, and so on), has become an addictive hobby. Just as some people collect stamps, other people collect virtual friends.

Your task is to observe the interactions on such a website and keep track of the size of each person's network.

Assume that every friendship is mutual. If Fred is Barney's friend, then Barney is also Fred's friend.

Input
Input file contains multiple test cases.
The first line of each case indicates the number of test friendship nest.
each friendship nest begins with a line containing an integer F, the number of friendships formed in this frindship nest, which is no more than 100 000. Each of the following F lines contains the names of two people who have just become friends, separated by a space. A name is a string of 1 to 20 letters (uppercase or lowercase).
Output
Whenever a friendship is formed, print a line containing one integer, the number of people in the social network of the two people who have just become friends.
Sample Input
13Fred BarneyBarney BettyBetty Wilma
Sample Output
234

输入的是字符串,我们需要用一个map来存放我们给这个字符串人为的编号。

其他的和上一题差不多。只需要用一个con[]数组记录以当前结点为根的集里有多少人就好了。

#include <iostream>#include <cstdio>#include <map>#include <cstring>using namespace std;int f[100086];int con[100086];int fin(int a){    if(a==f[a])    {        return a;    }    return f[a]=fin(f[a]);}int main(){    int t;    while(scanf("%d",&t)!=EOF)    {        while(t--)        {            int n,i;            scanf("%d",&n);            string xxx,yyy;            map<string,int> asd;            for(i=0;i<=n;i++)            {                f[i]=i;                con[i]=1;            }            asd.clear();            int low=1;            for(i=0;i<n;i++)            {                cin>>xxx>>yyy;                if(!asd[xxx])                {                    asd[xxx]=low++;                }                if(!asd[yyy])                {                    asd[yyy]=low++;                }                int a=asd[xxx];                int b=asd[yyy];                int x=fin(a);//这里把上一题的conbine函数写到main里面了                int y=fin(b);                if(x!=y)                {                    f[x]=y;                    con[y]+=con[x];                    printf("%d\n",con[y]);                }                else                {                    printf("%d\n",con[x]);                }            }        }    }    return 0;}

HDU--1892 AbugsLife

Background
Professor Hopper is researching the sexual behavior of a rare species of bugs. He assumes that they feature two different genders and that they only interact with bugs of the opposite gender. In his experiment, individual bugs and their interactions were easy to identify, because numbers were printed on their backs.

Problem
Given a list of bug interactions, decide whether the experiment supports his assumption of two genders with no homosexual bugs or if it contains some bug interactions that falsify it.

Input
The first line of the input contains the number of scenarios. Each scenario starts with one line giving the number of bugs (at least one, and up to 2000) and the number of interactions (up to 1000000) separated by a single space. In the following lines, each interaction is given in the form of two distinct bug numbers separated by a single space. Bugs are numbered consecutively starting from one.
Output
The output for every scenario is a line containing "Scenario #i:", where i is the number of the scenario starting at 1, followed by one line saying either "No suspicious bugs found!" if the experiment is consistent with his assumption about the bugs' sexual behavior, or "Suspicious bugs found!" if Professor Hopper's assumption is definitely wrong.
Sample Input
23 31 22 31 34 21 23 4
Sample Output
Scenario #1:Suspicious bugs found!Scenario #2:No suspicious bugs found!          
Hint
Huge input,scanf is recommended.

这道题是带权值的并查集,需要一定的想法。当然可以按照二分图去做,不过这里讲并查集,就用并查集吧。

这里设置father[]为并查集数组,rank[]代表的是子节点到根节点的距离。rank[i]=0代表和根节点同性,为1则异性。


#include <iostream>#include<cstdio>#include<string>#include<cstring>using namespace std;int Father[2005],Rank[2005];bool flag;int Find(int x){if (Father[x]!=x){int fx=Find(Father[x]);Rank[x]=(Rank[x]+Rank[Father[x]])%2;//更新集合关系Father[x]=fx;}    return Father[x];}void Union(int x,int y){    int fx=Find(x),fy=Find(y);    if(fx==fy)    {        if(Rank[x]==Rank[y])            flag=true;        return;    }    Father[fy]=fx;    Rank[fy]=(Rank[x]+Rank[y]+1)%2;}int main(){int t;    cin>>t;int i;    for (int cases=1;cases<=t;cases++)    {        flag=false;int n,k;        for (i=0;i<2005;i++)            Father[i]=i,Rank[i]=0;        cin>>n>>k;        for (i=0;i<k;i++)        {int x,y;            cin>>x>>y;            if (flag)continue;            Union(x,y);        }        cout<<"Scenario #"<<cases<<":\n";        if (flag) cout<<"Suspicious bugs found!\n\n";        else cout<<"No suspicious bugs found!\n\n";    }    return 0;}


设开始数据是这样,2,3,4,5都为1的子节点。

rank[i]=0123rank[i]=145 

现在输入数据 6 3,那么就会变成

rank[i]=0654rank[i]=1123

如果是3 6,那么就是

rank[i]=0123
rank[i]=1
45
6

由此可见,两个数的相对顺序没有什么大的影响,不过经常变换的话运算次数会增多。

注意的是,上面的变换只在用到的时候才会进行,而不是全换的。


这看了三道题,并查集应该不在话下了吧?