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
既然要暴枚,那肯定要优化。本题只关心城市的无向连通性,于是, A 与 B 连通则表示 A 与 B 在同一个集合里。一个连通块就被抽象成了一个集合。
于是每次去掉一条边,再将整个图并集。如果有两个连通块(集合)存在,就说明这条边是答案之一。
代码
预处理与输入
定义一个边结构。
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;
}
黄题就不加防作弊了。
完结散花
PCwqyy

浙公网安备 33010602011771号