并查集专题(附并查集模板)P3367 【模板】并查集 P1656 炸铁路

并查集模板

f数组要初始化
auto find(auto x)
{
    if(f[x]==x) return x;
    else return f[x]=find(f[x])路径压缩,同一条路上都归到一个点上
}

void unionset(auto a,auto b)
{
    f[find(a)]=find(b);   auto会自动适配数据类型
}

 P3367 【模板】并查集

题目描述

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

输入格式

第一行包含两个整数 N,M ,表示共有 N 个元素和 M 个操作。

接下来 M 行,每行包含三个整数 Zi​,Xi​,Yi​ 。

当 Zi​=1 时,将 Xi​ 与 Yi​ 所在的集合合并。

当 Zi​=2 时,输出 Xi​ 与 Yi​ 是否在同一集合内,是的输出 Y ;否则输出 N 。

输出格式

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

输入输出样例

输入 

4 7
2 1 2
1 1 2
2 1 2
1 3 4
2 1 4
1 2 3
2 1 4

输出 

N
Y
N
Y

思路 :就是并查集模板的套用,首先先给f数组初始化,接着就是对输入的数字判断,看是合并,还是查看是否同组,然后直接调用函数就行了

#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
int n,k;
int z,x,y;
int f[10010];
vector<int> vv(10010,1);初始化为1

int find(int x)
{
	if(f[x]==x) return x;
	return f[x]=find(f[x]);
}

void unity(int x,int y)
{
	x=find(x),y=find(y);   按秩合并,直接用模板的也行,用这个的目的是合并的时候下面数字少的
	if(x==y) return;       祖先认下面数字多的祖先为祖先,这样查找快点
	if(vv[x]>vv[y]) swap(x,y);
	f[x]=y;
	vv[x]+=vv[y];
}

int main()
{
	cin>>n>>k;
	for(int i=1;i<=n;i++) f[i]=i;数组初始化,即认自己为祖先
	while(k--)
	{
		cin>>z>>x>>y;
		if(z==1) unity(x,y);
		else
		{
			x=find(x),y=find(y);
			if(x==y) cout<<"Y"<<endl;
			else cout<<"N"<<endl;
		}
	}
	return 0;
}

P1656 炸铁路

题目描述

A 国派出将军 uim,对 B 国进行战略性措施,以解救涂炭的生灵。

B 国有 n 个城市,这些城市以铁路相连。任意两个城市都可以通过铁路直接或者间接到达。 

uim 发现有些铁路被毁坏之后,某两个城市无法互相通过铁路到达。这样的铁路就被称为 key road。

uim 为了尽快使该国的物流系统瘫痪,希望炸毁铁路,以达到存在某两个城市无法互相通过铁路到达的效果。

然而,只有一发炮弹(A 国国会不给钱了)。所以,他能轰炸哪一条铁路呢?

输入格式

第一行 n,m(1≤n≤150,1≤m≤5000),分别表示有 n 个城市,总共 m 条铁路。

以下 m 行,每行两个整数 a,b,表示城市 a 和城市 b 之间有铁路直接连接。

输出格式

输出有若干行。

每行包含两个数字 a,b,其中a<b,表示 〈a,b〉 是 key road。

请注意:输出时,所有的数对 〈a,b〉 必须按照 a 从小到大排序输出;如果a 相同,则根据 b 从小到大排序。

输入输出样例

输入 

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

输出 

1 2
5 6

输入 

10 9
2 1
3 1
4 1
10 4
9 4
6 3
7 3
8 3
5 3
​​​​​​​

输出 

1 2
1 3
1 4
3 5
3 6
3 7
3 8
4 9
4 10

思路:每次从其中拿走一条边,然后就套用并查集的模板,把剩下的边合并,然后就是遍历数组,如果会出现祖先不同的情况,就输出拿走的那一条边

有几点要注意一下,就是要给数据排序,还有a有可能大于b,针对这个要判断一下

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef pair<int,int>PII;
int n,m;
PII q[5010];
int f[160];

bool compare(PII a,PII b)
{
	if(a.first==b.first) return a.second<b.second;
	return a.first<b.first;
}

int find(int x)
{
	if(f[x]==x) return x;
	return f[x]=find(f[x]);
}

void unionset(int a,int b)
{
	f[find(a)]=find(b);
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		cin>>q[i].first>>q[i].second;
		if(q[i].first>q[i].second) swap(q[i].first,q[i].second);
	}
	sort(q+1,q+1+m,compare);
	for(int i=1;i<=m;i++)
	{
		for(int j=1;j<=n;j++) f[j]=j;
		for(int j=1;j<=m;j++)
		{
			if(i!=j) unionset(q[j].first,q[j].second);
		}
		
		for(int j=2;j<=n;j++)
		{
			if(f[find(j)]!=f[find(j-1)])
			{
				cout<<q[i].first<<" "<<q[i].second<<endl;
				break;
			}
		}
	}
	return 0;
}

还有一道简单的并查集题目,蓝桥杯的,合根植物,可以看我另一个博客

https://blog.csdn.net/jia_jia_LL/article/details/136978255?spm=1001.2014.3001.5501

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值