模板-并查集 p3367

题目描述
如题,现在有一个并查集,你需要完成合并和查询操作。

输入格式
第一行包含两个整数 N,MN,M ,表示共有 NN 个元素和 MM 个操作。

接下来 MM 行,每行包含三个整数 Z_i,X_i,Y_i

当 Z_i=1时,将 X_i 与 Y_i所在的集合合并。

当 Z_i=2 时,输出 X i 与 Y_i 是否在同一集合内,是的输出 Y ;否则输出 N 。

输出格式
对于每一个 Z_i=2 的操作,都有一行输出,每行包含一个大写字母,为 Y 或者 N 。

输入输出样例
输入 #1复制
4 7
2 1 2
1 1 2
2 1 2
1 3 4
2 1 4
1 2 3
2 1 4
输出 #1复制
N
Y
N
Y
说明/提示
对于30% 的数据,N≤10,M≤20
对于70% 的数据,N≤100,M≤10^3
对于 100% 的数据,1≤N≤10 ^4
1≤M≤2×10 ^5


#include<bits/stdc++.h>
using namespace std;
int n,m,z[66666],x[66666],y[66666];
int father[66666];
int find(int x)//找祖先
{
	if(father[x]==x) //它的祖先是它自己 
	{
		return x;
	}
	else
	{
		father[x]=find(father[x]);//继续往上找它的祖先 
	}
	return father[x]; //返回最终的祖先 
}

void work1(int x,int y)//合并,本质就是将他俩的祖先变成同一个 ,就可以归到同一个集合中去 
{
	father[find(y)] =find(x);//将y的祖先的祖先【即y的祖先的祖先它自己】变成x的祖先,使y和x的祖先相同 
} 
 
void work2(int x,int y)//查询: 
{
 	if(find(x)==find(y))//当它们有共同的祖先的时候,及证明在同一个家族中,同一个集合中 
 	{
 		cout<<"Y"<<endl;
	 }
	 else
	 {
	 	cout<<"N"<<endl;
	 }
}

int main()
{
	ios::sync_with_stdio(false);//关闭同步使cin,cout变快
	
	cin>>n>>m;
	//表示有n个元素,m个操作 
	for(int i=1;i<=m;i++)
	{
		cin>>z[i]>>x[i]>>y[i];
		
	 } 
	for(int i=1;i<=n;i++)//初始化并查集 
	{
	father[i]=i; //因为每一个数之间都还没有联系,所以它们的祖先就是它们自己 
	 } 


	//每次合并后查询的结果都会是不一样的 
	for(int i=1;i<=m;i++)
	{
		if(z[i]==1)
		{
			work1(x[i],y[i]);
		 } 
		else
		{
			work2(x[i],y[i]);
		}
	}
	return 0;
 } 

接下来是一道蓝桥杯的题目

题目描述:

小蓝要用七段码数码管来表示一种特殊的文字。
上图给出了七段码数码管的一个图示,数码管中一共有 7 段可以发光的二
极管,分别标记为 a, b, c, d, e, f, g。
小蓝要选择一部分二极管(至少要有一个)发光来表达字符。在设计字符
的表达时,要求所有发光的二极管是连成一片的。
例如:b 发光,其他二极管不发光可以用来表达一种字符。
例如:c 发光,其他二极管不发光可以用来表达一种字符。这种方案与上
一行的方案可以用来表示不同的字符,尽管看上去比较相似。
例如:a, b, c, d, e 发光,f, g 不发光可以用来表达一种字符。
例如:b, f 发光,其他二极管不发光则不能用来表达一种字符,因为发光
的二极管没有连成一片。
请问,小蓝可以用七段码数码管表达多少种不同的字符?

答案:80

思路: dfs搜索所有状态,判断每种状态可不可行。判断的方法是把每条灯管当作一个节点,连边建图,每次并查集判断当前选中的灯管是否在同一个集合。

#include<iostream>
using namespace std;
int use[10];
int ans,e[10][10],father[10];

void init()//[i][j]其中i与j相邻 
{
	//a b c d e f g
	//1 2 3 4 5 6 7
	e[1][2]=e[1][6]=1;
	e[2][1]=e[2][3]=e[2][7]=1;
	e[3][2]=e[3][4]=e[3][7]=1;
	e[4][3]=e[4][5]=1;
	e[5][4]=e[5][6]=e[5][7]=1;
	e[6][1]=e[6][5]=e[6][7]=1;
}

int find(int a)//并查集,进行了路径压缩了,找到a的祖先(代表元素),不断往上查找 
{
	if(father[a]==a)//当它的祖先就是它自己 
	{
		return a;
	}
	father[a]=find(father[a]);//通过递归找到头节点 ,更新为它的头节点 
	return father[a];
 } 
 
 void dfs(int d)
 {
 	if(d>7)
 	{
 		for(int i=1;i<=7;i++)
 		{
 			father[i]=i;//初始化,一开始这些数字都没有关系,所以每个元素的头节点都是自己 
		}
		 
		for(int i=1;i<=7;i++)
		 {
		 	for(int j=1;j<=7;j++)
		 	{
		 		if(e[i][j]&&use[i]&&use[j])//如果相邻并且两个灯都是亮的 
		 		{
		 			int fx=find(i),fy=find(j);//找到i和j的祖先,fx和fy 
					 if(fx!=fy)//两人有不同的祖先 
					 {
					 	father[fx]=fy;//合并两个集合 //i的祖先fx的祖先指向j的祖先fy 
					 	//相当于father[find(x)]=find(y); 
					  } 
				 }
			 }
		 }
		 int k=0;
		 for(int i=1;i<=7;i++)
		 {
		 	if(use[i]&&father[i]==i)//要使得k==1,就要已开的灯中只有一个祖先【即灯连成一片】,当其他的灯的祖先都被设为同一个的时候,灯连成一片 
		 	{
		 		k++;
			 }
		 }
		 if(k==1)
		 {
		 	ans++;
		 }
		 return ;
		 
	 }
	use[d]=1;//打开d这个灯,继续开关下一个灯
	dfs(d+1);
	use[d]=0;//关闭d这个灯,继续开关下一个灯
	dfs(d+1);
	// dfs搜索使得一个一个灯地开,从1,开到最后一盏灯;再从2开到最后一盏灯;再从3开到最后一盏灯 
 }
int main()
{
	init();
	dfs(1);
	cout<<ans<<endl;
	return 0;
}

接下来一道ACM的题目:

Online Judge Online Exercise Online Teaching Online Contests Exercise Author
F.A.Q
Hand In Hand
Online Acmers
Forum | Discuss
Statistical Charts
Problem Archive
Realtime Judge Status
Authors Ranklist
C/C++/Java Exams
ACM Steps
Go to Job
Contest LiveCast
ICPC@China
Best Coder beta
VIP | STD Contests
Virtual Contests
DIY | Web-DIY beta
Recent Contests

Author ID
Password
Register new ID

(点击查看详情)杭电ACM-LCY算法培训“进阶班”
How Many Tables
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 69157 Accepted Submission(s): 34882

Problem Description
Today is Ignatius’ birthday. He invites a lot of friends. Now it’s dinner time. Ignatius wants to know how many tables he needs at least. You have to notice that not all the friends know each other, and all the friends do not want to stay with strangers.

One important rule for this problem is that if I tell you A knows B, and B knows C, that means A, B, C know each other, so they can stay in one table.

For example: If I tell you A knows B, B knows C, and D knows E, so A, B, C can stay in one table, and D, E have to stay in the other one. So Ignatius needs 2 tables at least.

Input
The input starts with an integer T(1<=T<=25) which indicate the number of test cases. Then T test cases follow. Each test case starts with two integers N and M(1<=N,M<=1000). N indicates the number of friends, the friends are marked from 1 to N. Then M lines follow. Each line consists of two integers A and B(A!=B), that means friend A and friend B know each other. There will be a blank line between two cases.

Output
For each test case, just output how many tables Ignatius needs at least. Do NOT print any blanks.

Sample Input
2
5 3
1 2
2 3
4 5

5 1
2 5

Sample Output
2
4



#include<iostream>
using namespace std;

int father[10000];
int kk[100];

void init(int n)
{
	for(int i=1;i<=n;i++)
	{
		father[i]=i;
		kk[i]=1;//表示i所处集合的元素的个数,一开始每个数字单独成为一个集合 
	}
}
int find(int x)
{
	if(father[x]!=x)
	{
		father[x]=find(father[x]);
	}
	return father[x];
}
void together(int x,int y)
{
	if(find(x)!=find(y))
	{
		father[x]=y;
		kk[y]+=kk[x];
	}
}

int main()
{
	int T,M,k,n,m,x,y;
	cin>>T;
	while(T--)
	{
		scanf("%d %d",&n,&m);
		
		//初始化!! 
		init(n);
	
		k=0;
		while(m--)
		{
			scanf("%d %d",&x,&y);
			together(x,y);
		}
		
		//问题是如何找谁和谁一组呢???:如下解决
		//遍历数组,找出有多少个子集合 
		for(int i=1;i<=n;i++)
		{
			if(father[i]==i)
			{
				k++;
			}
		} 
		printf("%d\n",k);
	}
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值