题目
小明的实验室有N台电脑,编号1~N。原本这N台电脑之间有N-1条数据链接相连,恰好构成一个树形网络。在树形网络上,任意两台电脑之间有唯一的路径相连。
不过在最近一次维护网络时,管理员误操作使得某两台电脑之间增加了一条数据链接,于是网络中出现了环路。环路上的电脑由于两两之间不再是只有一条路径,使得这些电脑上的数据传输出现了BUG。
为了恢复正常传输。小明需要找到所有在环路上的电脑,你能帮助他吗?
输入
第一行包含一个整数N。
以下N行每行两个整数a和b,表示a和b之间有一条数据链接相连。
对于30%的数据,1 <= N <= 1000
对于100%的数据, 1 <= N <= 100000, 1 <= a, b <= N
输入保证合法。
输出
按从小到大的顺序输出在环路上的电脑的编号,中间由一个空格分隔。
样例输入
5
1 2
3 1
2 4
2 5
5 3
样例输出
1 2 3 5
解题思路
开始用的是最基础的方法,struct记录每一个结点连接的结点数目和相连结点的具体编号,深度搜索加上map记录走过的“路”,如果首尾相接且“不走回头路”即为环形,但是每个struct需要预分配一定空间,且当结点数目增加,所占内存也会明显增加,会导致内存超限。
之后学习了无向图的方法,无向图中的环形就是断开非环形结点后,所有度为2的结点所组成的。因此,可以将度为1的结点存入队列,开始遍历,如果它们相邻的结点在去除了它们后,度仍为1,说明这个结点仍为非环上的结点,继续放入队列进行遍历。在所有边缘的残余点都被去除后,剩下的度为2的点即为环上点。
难点
对无向图的环的判断方法:将度为1的点存入队列,不断去除度为1的点,再将新的度为1的点存入队列,直到队列为空。最终剩下的度为2的点都是环上点。
代码
#include<bits/stdc++.h>
using namespace std;
vector <int> ad[100001];//可变长度的二维int数组
queue <int> points;//把当前度为1需要去除的点存入其中
int num[100001];//记录每一个结点当前的度
int used[100001];//该点是否已经去除
int n;
void topology(){
int i,j,temp,t;
for (i=1;i<=n;i++)//对队列初始化,也就是对初始度为1的点进行去除
if (num[i]==1)//只有一个度
points.push(i);
while (points.empty()==0){
temp = points.front();
used[temp] = 1;//已经去除,之后不用再遍历
points.pop();
for (j=0;j<num[temp];j++)
{
t = ad[temp][j];//取出的值
if ((--num[t])==1 && used[t]==0)//一定要“实际上”剪去这个连接
{ //去掉temp,t之间的连接,还剩下1
//那么它一定不是环上的结点,需要继续剪去
used[t] = 1;
points.push(t);
}
}
}
}
int main(){
int i,temp1,temp2,has_printed=0;
scanf("%d",&n);
for (i=0;i<n;i++){
scanf("%d %d",&temp1,&temp2);
ad[temp1].push_back(temp2);//vector要push_back
ad[temp2].push_back(temp1);//不能用ad[temp1][num[temp1]++] = temp2;
num[temp1]++;
num[temp2]++;//邻接矩阵
}
topology();
for (i=1;i<=n;i++)
{
if (num[i]==2)
{
if (has_printed==1)
printf(" ");
printf("%d",i);
has_printed = 1;
}
}
return 0;
}