三道题讲并查集

该篇博客详细讲解了如何利用并查集解决HDU1232畅通工程问题。内容涉及如何通过并查集算法分析和求解使全省任意两个城镇都能实现交通的最少新增道路数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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 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
所谓并查集呢,就是用一个数组,把一个元素的父节点存起来。父节点是什么呢,一般就是根据题目来说对当前点有指向关系的节点。 比如在这道题中,如果节点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
1
3
Fred Barney
Barney Betty
Betty Wilma
Sample Output
2
3
4

输入的是字符串,我们需要用一个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
2
3 3
1 2
2 3
1 3
4 2
1 2
3 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]=0123
rank[i]=145 

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

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

如果是3 6,那么就是

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

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

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


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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值