并查集总结(一)

A Bug's Life

Time Limit: 15000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 10493    Accepted Submission(s): 3411


Problem Description
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!

首先这道题的想法就是类似于食物链的那道题,我们就是类似于把他们看成两个不同的集合,然后合并相同部分的集合,最后再判断本来应该属于不同种类的他们的祖先是不是同一个了,如果是同一个的话,那么就是错误的了。

#include<stdio.h>
int par[4010];
int find(int x){
	return par[x]==x?x:par[x]=find(par[x]);
}
void merge(int x,int y){
	int tx,ty;
	tx=find(x);
	ty=find(y);
	if(tx!=ty) par[tx]=ty;
}
int main(){
	int T;
	int n,m,j=1;
	int x,y;
	scanf("%d",&T);
	for(int i=1;i<=T;i++){
		int flag=0;
		scanf("%d%d",&n,&m);
		for(int i=1;i<=2*n;i++) par[i]=i;
		while(m--){
			scanf("%d%d",&x,&y);
			if(find(x)==find(y)){
				flag=1;  
			}
			else{
				merge(x,y+n);
				merge(x+n,y);
			}
		}
		printf("Scenario #%d:\n",i);
		if(flag) printf("Suspicious bugs found!\n");
		else puts("No suspicious bugs found!");
		puts("");
	}
}

Virtual Friends

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 6086    Accepted Submission(s): 1721


Problem Description
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来储存人名,map在这里的作用是为了给每个人来标记序号,从而就不会重复了。

这里注意,map不要忘记清空。

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<map>
using namespace std;
#define maxn 111111
int par[maxn],num[maxn];	//num代表现在认识了几个人; 
map<string,int> mp;
void init(){
	for(int i=1;i<=maxn;i++){
		par[i]=i;
		num[i]=1;
	}
}
int find(int x){
	return x==par[x]?x:par[x]=find(par[x]);
}
void merge(int x,int y){
	int tx=find(x);
	int ty=find(y);
	if(tx!=ty){
		par[tx]=ty;
		num[ty]+=num[tx];
		printf("%d\n",num[ty]);
	}
	else{
		printf("%d\n",num[ty]);
	}
}
int main(){
	int T,n;
	char a[33],b[33];
	while(~scanf("%d",&T)){
		while(T--){
			int index=1;
			init();
			scanf("%d",&n);
			mp.clear();
			for(int i=0;i<n;i++){
				scanf("%s%s",a,b);
				if(mp[a]==0){
					mp[a]=index++;
				}
				if(mp[b]==0){
					mp[b]=index++;
				}
				merge(mp[a],mp[b]);
			}
		}
	}
}

Dragon Balls

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3857    Accepted Submission(s): 1486


Problem Description
Five hundred years later, the number of dragon balls will increase unexpectedly, so it's too difficult for Monkey King(WuKong) to gather all of the dragon balls together. 

His country has N cities and there are exactly N dragon balls in the world. At first, for the ith dragon ball, the sacred dragon will puts it in the ith city. Through long years, some cities' dragon ball(s) would be transported to other cities. To save physical strength WuKong plans to take Flying Nimbus Cloud, a magical flying cloud to gather dragon balls. 
Every time WuKong will collect the information of one dragon ball, he will ask you the information of that ball. You must tell him which city the ball is located and how many dragon balls are there in that city, you also need to tell him how many times the ball has been transported so far.
 

Input
The first line of the input is a single positive integer T(0 < T <= 100). 
For each case, the first line contains two integers: N and Q (2 < N <= 10000 , 2 < Q <= 10000).
Each of the following Q lines contains either a fact or a question as the follow format:
  T A B : All the dragon balls which are in the same city with A have been transported to the city the Bth ball in. You can assume that the two cities are different.
  Q A : WuKong want to know X (the id of the city Ath ball is in), Y (the count of balls in Xth city) and Z (the tranporting times of the Ath ball). (1 <= A, B <= N)
 

Output
For each test case, output the test case number formated as sample output. Then for each query, output a line with three integers X Y Z saparated by a blank space.
 

Sample Input
  
  
2 3 3 T 1 2 T 3 2 Q 2 3 4 T 1 2 Q 1 T 1 3 Q 1
 

Sample Output
  
  
Case 1: 2 3 0 Case 2: 2 2 1 3 3 2
 


这道题是我认为现在碰到的一道新的并查集的题目。

题目的大致意思是:一开始n个龙珠都被放在n个城市里。

然后有Q次询问,一种是:“T A B ",代表和A在一起的球全被运到B所在的城市里去了。

Q  A:就代表的是询问,对于每次询问,你要输出A球所在的城市X,以及X中有几个球,以及A球被转运了几次。

对于前两个询问我们是比较容易的处理的。于是我学习了一下怎么求A球被转运了几次。

这里我们用到了路径压缩的知识,其实合并两个集合的次数就是可以看做第一个集合的根节点的移动次数加1,这里我们可以在递归调用的时候改变移动次数。

其实我对这里的改变次数还不是理解的很透彻。

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 11111
int par[maxn],num[maxn],tmove[maxn];
void init(){
	for(int i=1;i<=maxn;i++) par[i]=i;
}
int find(int x){
	if(par[x]==x) return x;
		int temp=par[x];
		par[x]=find(par[x]);
		//这里的目的是为了全部更新子节点的移动次数为根的加1; 
		tmove[x]+=tmove[temp];
		return par[x];
}
void merge(int x,int y){
	int tx,ty;
	tx=find(x);
	ty=find(y);
	// 注意下面的判断条件,只有当两个是不同时,才是tmove[tx]=1; 因为此时是第一次移动; 
	if(tx!=ty){
		par[tx]=ty;
		tmove[tx]=1;
		num[ty]+=num[tx];
		num[tx]=0;
	}
}
int main(){
	int T,n,q,j=1;
	char c[5];
	scanf("%d",&T);
	while(T--){
		bool ff=false;
		init();
		fill(num,num+maxn,1);
		memset(tmove,0,sizeof(tmove));
		int a,b;
		scanf("%d%d",&n,&q);
		while(q--){
			scanf("%s",c);
			if(c[0]=='T'){
				scanf("%d%d",&a,&b);
				merge(a,b);
			}
			else if(c[0]=='Q'){
				scanf("%d",&a);
				int t=find(a);
				if(!ff){
					printf("Case %d:\n",j++); ff=true;
				}
				printf("%d %d %d\n",t,num[t],tmove[a]);
			}
		}
	}
}

//附上详细解释的代码:

#include <stdio.h>
#include <string.h>
#define maxn 10005

int transport[maxn];//transport[i]表示i球转移了多少次
int ball[maxn];//ball[i]表示i球在哪个城市,其实是并查集中的集合 
int city[maxn];//city[i]表示i号城市有几个球 
/*带权并查集*/
void initialize()
{
	for(int i = 0 ; i <= maxn - 1 ; ++i)
	{
		ball[i] = i;//初始i号球在i城市 
		city[i] = 1;//初始i城市只有一个球 
		transport[i] = 0;//初始i号球没有经过任何转移 
	}
}
int find_root(int x)
{	
    //ball[x]是x的父节点 
	if(x == ball[x])
		return x;
	int temp = ball[x]; 
	ball[x] = find_root(ball[x]);
	transport[x] += transport[temp];
	//注意这条语句和上条顺序不可颠倒,这一过程是在回溯时完成的 
	return ball[x];	
}
void Union(int x,int y)
{
	int root1 = find_root(x);
	int root2 = find_root(y);
	if(root1 != root2)
	{
		ball[root1] = root2;
		transport[root1] = 1;//第一次移动 
		city[root2] += city[root1]; 
		city[root1] = 0;//移动完毕后置0 
		//这里的city其实是并查集的树的高度。 
	}
}
int main()
{
	int Case;
	int balls,query;
	int from,to;
	char cmd[5];
	
	scanf("%d",&Case);
	int count = 0;
	while(Case--)
	{
		initialize();
		printf("Case %d:\n",++count);
		scanf("%d%d",&balls,&query);
		while(query--)
		{
			scanf("%s",cmd);
			if(cmd[0] == 'Q')
			{
				int k;
				scanf("%d",&k);
				int x = find_root(k);
				printf("%d %d %d\n",x,city[x],transport[k]);
			}
			if(cmd[0] == 'T')
			{
				scanf("%d%d",&from,&to);
				Union(from,to);
			}
		}
	}
	return 0;
}

虽然说对于一些算法的变式还不是很会用,但是多见识,多做题就会渐渐强大起来的吧,加油!


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值