咋一看是个图论,仔细一看其实是个并查集。
我们用并查集可以表示每个连通块,方法就是用并查集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;
}