题解 CF25D 【Roads not only in Berland】

咋一看是个图论,仔细一看其实是个并查集


我们用并查集可以表示每个连通块,方法就是用并查集merge操作来加边,同时如果加边不成功,即两个端点已经在同一连通块内,那么把这个边加进回收站del[]数组,等到后面输出答案时,这个有着半壁江山。del[]数组的大小就是最好的方案数。

然后我们扫描一遍并查集的f[]数组,统计连通块的个数(方法就是看有几个f[i]==i),连通块的个数后面要用。

再说后面的方案输出,我们只要清空回收站,然后把各个连通块合并成一个,具体的话——看代码罢。

代码在哪?就在下面

#include<iostream>
#include<cstdio>
#include<cstring>
#define F first
#define S second
using namespace std;
const int N=10005;
int f[N],gr[N],n,tot,cnt,now;
pair<int,int> d[N];
int find(int x) {//并查集找祖宗
	if(f[x]!=x) f[x]=find(f[x]);
	return f[x];
}
bool merge(int x,int y) {//并查集合并
	x=find(x);
	y=find(y);
	if(x!=y) f[x]=y;
	else return 1;
	return 0;
}
void del(int i) {//处理+输出函数
	printf("%d %d %d %d
",d[i].F,d[i].S,gr[now],gr[cnt]);//输出回收站当前边、当前合并的连通块祖宗
	merge(gr[now++],gr[cnt]);//合并连通块并将当前连通块定位到下一个
}
int main() {
	scanf("%d",&n);
	for(int i=1;i<=n;i++) f[i]=i;
	for(int i=1;i<n;i++) {
		int x,y;
		scanf("%d%d",&x,&y);
		if(merge(x,y)) d[++tot]=make_pair(x,y);//扔进回收站
	}
	for(int i=1;i<=n;i++) if(f[i]==i) gr[++cnt]=i;//统计连通块的祖宗
	now=1;
	printf("%d
",tot);
	for(int i=1;i<=cnt-1;i++)
		del(i);//处理+输出
	return 0;
}

原文地址:https://www.cnblogs.com/ahawzlc/p/12845009.html