并查集(水题)

A. 是不是亲戚

题目描述

若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,现在给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系。

规定:x 和 y 是亲戚,y 和 z 是亲戚,那么 x 和 z 也是亲戚。如果 x,y 是亲戚,那么 x 的亲戚都是 y 的亲戚,y 的亲戚也都是 x 的亲戚。

输入:

第一行:三个整数 n,m,p,(n≤5000,m≤5000,p≤5000 ),分别表示有 n 个人,m 个亲戚关系,询问 p 对亲戚关系。

以下 m 行:每行两个数 Mi​,Mj​,1≤Mi​,Mj​≤N,表示 Mi​ 和 Mj​ 具有亲戚关系。

接下来 p 行:每行两个数 Pi​,Pj​,询问 Pi​ 和 Pj​ 是否具有亲戚关系。

输出:

P行,每行一个 Yes 或 No。表示第 i 个询问的答案为“具有”或“不具有”亲戚关系。

样例:

输入

6 5 3
1 2
1 5
3 4
5 2
1 3
1 4
2 3
5 6

输出

Yes
Yes
No

代码如下:

#include<bits/stdc++.h>
using namespace std;
int fa[5010];
int n,m,q;
int find(int x){
	if(fa[x]==x) return x;
	else return find(fa[x]);
}
void merge(int x,int y){
	int fx=find(x);
	int fy=find(y);
	if(fx!=fy){
		fa[fx]=fy;
	}
}
int main(){
	cin>>n>>m>>q;
	for(int i=1;i<=n;i++){
		fa[i]=i;
	}
	int x,y;
	for(int i=1;i<=m;i++){
		cin>>x>>y;
		merge(x,y);
	}
	for(int i=1;i<=q;i++){
		cin>>x>>y;
		if(find(x)==find(y)){
			cout<<"Yes"<<endl;
		}else{
			cout<<"No"<<endl;
		}
	}
	return 0;
}

B. 修路

题目描述:

某市调查城镇交通状况,得到现有城镇道路统计表。表中列出了每条道路直接连通的城镇。市政府 "村村通工程" 的目标是使全市任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要相互之间可达即可)。

请你计算出最少还需要建设多少条道路?

输入:

输入包含若干组测试数据,每组测试数据的第一行给出两个用空格隔开的正整数,分别是城镇数目 n 和道路数目 m (输入 n=0 表示测试数据结束);

随后的 m 行对应 m 条道路,每行给出一对用空格隔开的正整数,分别是该条道路直接相连的两个城镇的编号。简单起见,城镇从 1 到 n 编号。

注意:两个城市间可以有多条道路相通。

对于 100% 的数据,保证 1≤n<1000 。

输出:

对于每组数据,对应一行一个整数。表示最少还需要建设的道路数目。

样例:

输入:

4 2
1 3
4 3
3 3
1 2
1 3
2 3
5 2
1 2
3 5
999 0
0

输出

1
0
2
998

代码如下:

#include<bits/stdc++.h>
using namespace std;
int fa[1010];
int n,m;
int find(int x){
	if(fa[x]==x) return x;
	else return fa[x]=find(fa[x]);
}
void merge(int x,int y){
	int fx=find(x);
	int fy=find(y);
	if(fx!=fy){
		fa[fx]=fy;
	}
}
int main(){
	while(1){
		cin>>n;
		if(n==0) break;
		cin>>m;
		for(int i=1;i<=n;i++) fa[i]=i;
		int x,y;
		for(int i=1;i<=m;i++){
			cin>>x>>y;
			merge(x,y);
		}
		int c=0;
		for(int i=1;i<=n;i++){
			if(fa[i]==i) c++;
		}
		cout<<c-1<<endl;
	}
	return 0;
}

C. 躲避拥堵的最佳路线

题目描述:

小明所在的城镇有 m 条路连接了 n 个区( n 个区的编号在 1∼n 的范围内),每条大道将两个区相连接,每条大道有一个拥挤度。

小明想要开车从 s 区去 t 区,请你帮他规划一条路线,使得经过道路的拥挤度的最大值最小。

输入:

第一行有四个用空格隔开的 n,m,s,t ,其含义见题目描述。

接下来 m 行,每行三个整数 u,v,w ,表示有一条大道连接区 u 和区 v,且拥挤度为 w ,道路为双向道路,两个方向都可以走。

两个区之间可能存在多条大道。

数据规模与约定:

对于 30% 的数据,保证 n≤10 。

对于 60% 的数据,保证 n≤100 。

对于 100% 的数据,保证 1≤n≤10^4,1≤m≤2×10^4,,w≤10^4,1≤s,t≤n。

且从 s 出发一定能到达 t 区。

输出:

输出一行一个整数,代表最大的拥挤度。

样例:

输入:

3 3 1 3
1 2 2
2 3 1
1 3 3

输出:

2

代码如下:

#include<bits/stdc++.h>
using namespace std;
int fa[10010];
int n,m,s,t,l=INT_MAX,r=INT_MIN,mid;
struct city{
	int x,y,len; 
}a[20010]; 
int find(int x){
	if(fa[x]==x) return x;
	else return fa[x]=find(fa[x]);
}
void merge(int x,int y){
	int fx=find(x);
	int fy=find(y);
	if(fx!=fy){
		fa[fx]=fy;
	}
}
bool check(int mid){
	for(int i=1;i<=n;i++){
		fa[i]=i;
	}
	for(int i=1;i<=m;i++){
		 if(a[i].len<=mid){
		 	merge(a[i].x,a[i].y);
		 }
	}
	return find(s)==find(t);
}
int main(){
	cin>>n>>m>>s>>t;
	for(int i=1;i<=m;i++){
		cin>>a[i].x>>a[i].y>>a[i].len;
		l=min(l,a[i].len);
		r=max(r,a[i].len);
	} 
	while(l<=r){
		mid= l + r >> 1;
		if(check(mid)) r=mid-1;
		else l=mid+1;
	}
	cout<<l;
	return 0;
}

D. 团队数量

题目描述:

芝加哥组织了一场激烈的军事竞赛,很多国家的军人慕名而来,他们要么是队友,要么是敌人。

现建立如下规则:

  1. 我的队友的队友,是我的队友;

  2. 我的敌人的敌人也是我的队友;

两个人只要是队友,就认为他们属于同一团队,现给你若干参赛军人之间的关系,请问:最多有多少个团队?

输入:

第一行是一个整数 N (2≤N≤1000),表示参赛的人数(从 1 编号到 N )。

第二行 M (1≤M≤5000),表示关于参赛者的关系信息的条数。

以下 M 行,每行可能是 F p q 或是 E p q(1≤p,q≤N),F 表示 p 和 q 是队友,E 表示 p 和 q 是敌人。

输入数据保证不会产生信息的矛盾。

输出:

输出文件只有一行,表示最大可能的团队数。

样例:

输入:

6
4
E 1 4
F 3 5
F 4 6
E 1 2

输出:

3

代码如下:

#include<bits/stdc++.h>
using namespace std;
int f[5010];
int n,m;
int find(int x){
	return x == f[x]?x:f[x]=find(f[x]);
}
void merge(int x,int y){
	int fx=find(x);
	int fy=find(y);
	if(fx!=fy){
		f[fx]=fy;
	}
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=2*n;i++){
		f[i]=i;
	}
	char c;
	int x,y;
	for(int i=1;i<=m;i++){
		cin>>c>>x>>y;
		if(c=='F') merge(x,y);
		else{
			merge(y+n,x);
			merge(x+n,y);
		}
	}
	int r=0;
	for(int i=1;i<=n;i++){
		if(f[i]==i) r++;
	}
	cout<<r;
	return 0;
}

E. 关押罪犯(这题是NOIP原题,有亿点难,新手勿做!)

题目描述:

S 城现有两座监狱,一共关押着 N 名罪犯,编号分别为 1−N。他们之间的关系自然也极不和谐。很多罪犯之间甚至积怨已久,如果客观条件具备则随时可能爆发冲突。我们用“怨气值”(一个正整数值)来表示某两名罪犯之间的仇恨程度,怨气值越大,则这两名罪犯之间的积怨越多。如果两名怨气值为 c 的罪犯被关押在同一监狱,他们俩之间会发生摩擦,并造成影响力为 c 的冲突事件。

每年年末,警察局会将本年内监狱中的所有冲突事件按影响力从大到小排成一个列表,然后上报到 S 城 Z 市长那里。公务繁忙的 Z 市长只会去看列表中的第一个事件的影响力,如果影响很坏,他就会考虑撤换警察局长。

在详细考察了 N 名罪犯间的矛盾关系后,警察局长觉得压力巨大。他准备将罪犯们在两座监狱内重新分配,以求产生的冲突事件影响力都较小,从而保住自己的乌纱帽。假设只要处于同一监狱内的某两个罪犯间有仇恨,那么他们一定会在每年的某个时候发生摩擦。

那么,应如何分配罪犯,才能使 Z 市长看到的那个冲突事件的影响力最小?这个最小值是多少?

输入:

每行中两个数之间用一个空格隔开。

第一行为两个正整数 N,M,分别表示罪犯的数目以及存在仇恨的罪犯对数。

接下来的 M 行每行为三个正整数 aj,bj​,cj​,表示 aj​ 号和 bj​ 号罪犯之间存在仇恨,其怨气值为 cj​。

数据保证 1<aj​≤bj​≤N,0<cj​≤10^9,且每对罪犯组合只出现一次。

输出:

共 1 行,为 Z 市长看到的那个冲突事件的影响力。如果本年内监狱中未发生任何冲突事件,请输出 0

样例:

输入:

4 6
1 4 2534
2 3 3512
1 2 28351
1 3 6618
2 4 1805
3 4 12884

输出:

3512

代码如下:

#include<bits/stdc++.h>
using namespace std;
int f[40010];
int n,m;
struct node{
	int x,y,v;
}a[100010]; 
int find(int x){
	return x == f[x]?x:f[x]=find(f[x]);
}
bool cmp(node n1,node n2){
	return n1.v>n2.v;
}
void merge(int x,int y){
	int fx=find(x);
	int fy=find(y);
	if(fx!=fy){
		f[fx]=fy;
	}
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		cin>>a[i].x>>a[i].y>>a[i].v;
	}
	sort(a+1,a+m+1,cmp);
	for(int i=1;i<=n*2;i++){
		f[i]=i; 
	}
	for(int i=1;i<=m;i++){
		if(find(a[i].x)==find(a[i].y)){
			cout<<a[i].v;
			return 0;
		}else{
			merge(a[i].x+n,a[i].y);
			merge(a[i].y+n,a[i].x);
		}
	}
	cout<<0;
	return 0;
}

F. 最短网络 Agri-Net(USACO3.1)(更难!!!)

题目描述:

Farmer John 被选为他们镇的镇长!他其中一个竞选承诺就是在镇上建立起互联网,并连接到所有的农场。当然,他需要你的帮助。

FJ 已经给他的农场安排了一条高速的网络线路,他想把这条线路共享给其他农场。为了用最小的消费,他想铺设最短的光纤去连接所有的农场。

你将得到一份各农场之间连接费用的列表,你必须找出能连接所有农场并所用光纤最短的方案。每两个农场间的距离不会超过 10^5 。

输入:

第一行农场的个数 N(3≤N≤100)。

接下来是一个 N × N 的矩阵,表示每个农场之间的距离。理论上,他们是 N 行,每行由 N 个用空格分隔的数组成,实际上,由于每行 80 个字符的限制,因此,某些行会紧接着另一些行。当然,对角线将会是 0 ,因为不会有线路从第 i 个农场到它本身。

输出:

只有一个输出,其中包含连接到每个农场的光纤的最小长度。

样例:

输入:

4
0 4 9 21
4 0 8 17
9 8 0 16
21 17 16 0

输出:

28

代码如下:

#include<bits/stdc++.h>
using namespace std;
int f[110];
int n,t,k;
struct node{
	int x,y,len;
}a[6000]; 
int find(int x){
	return x == f[x]?x:f[x]=find(f[x]);
}
bool cmp(node n1,node n2){
	return n1.len<n2.len;
}
void merge(int x,int y){
	int fx=find(x);
	int fy=find(y);
	if(fx!=fy){
		f[fx]=fy;
	}
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			cin>>t;
			if(i<j){
				k++;
				a[k].x=i;
				a[k].y=j;
				a[k].len=t;
			}
		}
	}
	sort(a+1,a+k+1,cmp);
	for(int i=1;i<=n;i++){
		f[i]=i;
	}
	int s=0,c=0;
	for(int i=1;i<=k;i++){
		int fx=find(a[i].x);
		int fy=find(a[i].y);
		if(fx!=fy){
			f[fx]=fy;
			c++;
			s=s+a[i].len;
		}
		if(c==n-1){
			cout<<s;
			break;
		}
	}
	return 0;
}

G. 立方体积木Cube Stacking(也挺难)

题目描述:

约翰和贝茜在玩一个方块游戏。编号为 1……n 的 n ( 1≤n≤30000 ) 个方块正放在地上,每个构成一个立方柱。
游戏开始后,约翰会给贝茜发出 P ( 1≤P≤100000 ) 个指令。指令有两种:
移动(M):将包含 X 的立方柱移动到包含 Y 的立方柱上。
统计(C):统计含 X 的立方柱中,在 X 下方的方块数目。
写个程序帮贝茜完成游戏。

输入:

第 1 行输入 P,之后 P 行每行输入一条指令,形式为 M X Y 或者 C X。
输入保证不会有将立方柱放在自己头上的指令。

输出:

输出共 P 行,对于每个统计指令,输出其结果。

样例:

输入:

6
M 1 6
C 1
M 2 4
M 2 6
C 3
C 4

输出:

1
0
2

代码如下:

#include<bits/stdc++.h>
using namespace std;
const int N=30010;
int f[N];
int dis[N];
int len[N];
int p;
int find(int x){
	if(f[x]==x) return x;
	else{
		int t=f[x];
		f[x]=find(f[x]);
		dis[x]=dis[x]+dis[t];
		return f[x];
	} 
}
void merge(int x,int y){
	int fx=find(x);
	int fy=find(y);
	if(fx!=fy){
		f[fx]=fy;
		dis[fx]=dis[fx]+len[fy];
		len[fy]=len[fy]+len[fx];
		len[fx]=0;
	}
}
int main(){
	cin>>p;
	for(int i=1;i<=30000;i++){
		f[i]=i;
		len[i]=1;
	}
	char c;
	int x,y;
	for(int i=1;i<=p;i++){
		cin>>c>>x;
		if(c=='C'){
			find(x);
			cout<<dis[x]<<endl;
		}else{
			cin>>y;
			merge(x,y);
		}
	} 
	return 0;
}

温馨提示:

这些"水题"的题目是作者复制的,代码是作者自己做的,看官们请自己做一遍,锻炼锻炼自己的写代码能力,还有!不了解并查集的请看——并查集概述呦!!!

本作者希望看官们能多多评论,谢谢啦!!!

!!!

!!!

!!!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值