题目 1841: 发现环

题目

小明的实验室有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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值