Loading

P1656

所属题目:P1656 炸铁路

前置代码

并查集

并查集是一种只关心元素对于集合的所属关系的数据结构。
它的原型是一棵树,使用 “父亲表示法” 表示,用数组 Fa[i] 保存 i 的父亲的编号。
每个孩子都有一个父亲,而一个家族的祖先便是这个家族的标识。
如果你我有着同样的祖先,则你和我是一个家族的。
特别的,祖先的父亲是自己。

初始化

先认为每个元素自成集合,即 Fa[i]=i ,并集的时候在把父亲设为目标集合的祖先。

找父亲

由于并查集只关心所属关系,所以我爸是你或你爸是我没什么区别。所以,我们在找祖先时可以做路径优化,就是忽略之前所有的父子关系,统一认为一个家族所有人都是这个家族祖先的直接孩子,包括祖先自己。
使用递归函数向上找父亲、祖父、曾祖父...
回溯的时候顺便把父亲的父亲设为找到的那个祖先。

int Find(int n)
{
	if(Fa[n]!=n)	Fa[n]=Find(Fa[n]);
	return Fa[n];
}

并集

只需要操作一下祖先即可。

void Merge(int a,int b)
{
	a=Find(a),b=Find(b);
	Fa[b]=a;
}

查集

如果你我有着同样的祖先,则你和我是一个家族的。

bool Judge(int a,int b)
{
	a=Find(a),b=Find(b);
	return (a==b);
}

题意简化

给定一张无向图,问图中去掉那些会让图不再连通。
注意答案要有序。

如何想到并查集

由于本题范围很小,枚举即可
\(\hspace{3.0cm}\)——kkksc03

既然要暴枚,那肯定要优化。本题只关心城市的无向连通性,于是, AB 连通则表示 AB 在同一个集合里。一个连通块就被抽象成了一个集合。
于是每次去掉一条边,再将整个图并集。如果有两个连通块(集合)存在,就说明这条边是答案之一。

代码

预处理与输入

定义一个边结构。

struct L{int F,T;}E[20010],O[20010];
bool Compare(L a,L b)
{
	if(a.F!=b.F)	return a.F<b.F;
	else			return a.T<a.T;
}

使用边集数组 E 存边, G 作指针。

scanf("%d%d",&N,&M);
int v,u;
for(int i=0;i<M;i++)
	scanf("%d%d",&v,&u),
	E[G++]=(L){v,u};

暴枚

for(int j=0;j<G;j++)
	{
		//初始化
		int o=0;
		for(int i=1;i<=N;i++)	Fa[i]=i;
		//合并边
		for(int i=0;i<G;i++)
			if(i!=j&&!Judge(E[i].F,E[i].T))
				Merge(E[i].F,E[i].T);
		//统计集合个数
		for(int i=1;i<=N;i++)
			if(Fa[i]==i)
				o++;
		//记录答案
		if(o==2)
			O[gO++]=(L){E[j].F,E[j].T};
	}

输出

记得排序。

std::sort(O,O+gO,Compare);
for(int i=0;i<gO;i++)
	printf("%d %d\n",std::min(O[i].F,O[i].T),
						 std::max(O[i].F,O[i].T));

完整代码

#include<cstdio>
#include<algorithm>
struct L{int F,T;}E[20010],O[20010];
bool Compare(L a,L b)
{
	if(a.F!=b.F)	return a.F<b.F;
	else			return a.T<a.T;
}
int Fa[2005],N,G,gO,T,M,Ans;
int Find(int n)
{
	if(Fa[n]!=n)	Fa[n]=Find(Fa[n]);
	return Fa[n];
}
void Merge(int a,int b)
{
	a=Find(a),b=Find(b);
	Fa[b]=a;
}
bool Judge(int a,int b)
{
	a=Find(a),b=Find(b);
	return (a==b);
}
int main()
{
	scanf("%d%d",&N,&M);
	int v,u;
	for(int i=0;i<M;i++)
		scanf("%d%d",&v,&u),
		E[G++]=(L){v,u};
	for(int j=0;j<G;j++)
	{
		int o=0;
		for(int i=1;i<=N;i++)	Fa[i]=i;
		for(int i=0;i<G;i++)
			if(i!=j&&!Judge(E[i].F,E[i].T))
				Merge(E[i].F,E[i].T);
		for(int i=1;i<=N;i++)
			if(Fa[i]==i)
				o++;
		if(o==2)
			O[gO++]=(L){E[j].F,E[j].T};
	}
	std::sort(O,O+gO,Compare);
	for(int i=0;i<gO;i++)
		printf("%d %d\n",std::min(O[i].F,O[i].T),
						 std::max(O[i].F,O[i].T));
	return 0;
}

黄题就不加防作弊了。
完结散花

posted @ 2022-08-27 14:45  PCwqyy  阅读(8)  评论(0)    收藏  举报  来源