蓝桥杯刷题022——发现环(拓扑排序、DFS/BFS)

news/2024/6/29 11:51:06 标签: 蓝桥杯, 宽度优先, 算法

2017国赛

题目描述

小明的实验室有 N 台电脑,编号1⋯N。原本这 N 台电脑之间有 N−1 条数据链接相连,恰好构成一个树形网络。在树形网络上,任意两台电脑之间有唯一的路径相连。

不过在最近一次维护网络时,管理员误操作使得某两台电脑之间增加了一条数据链接,于是网络中出现了环路。环路上的电脑由于两两之间不再是只有一条路径,使得这些电脑上的数据传输出现了 BUG。

为了恢复正常传输。小明需要找到所有在环路上的电脑,你能帮助他吗?

输入描述

输入范围:

第一行包含一个整数 N 。

以下 N 行每行两个整数 a,b,表示 a 和 b 之间有一条数据链接相连。

其中, 1≤N≤10^5,1≤a,b≤N。

输入保证合法。

输出描述

按从小到大的顺序输出在环路上的电脑的编号,中间由一个空格分隔。

输入输出样例

输入

5
1 2
3 1
2 4
2 5
5 3

输出

1 2 3 5

题目大意

有一个树,N个节点,编号l-N树中多了一条边,导致了环的产生找出所有环上的节点,按升序输出

树是一种特殊的图](无环图)
一个N个节点的树,边数是N-1

解题思路:拓扑排序
知识补充1:节点的入度和出度

  • 节点的入度:从别处进入该节点的边数,例如下图结点3的入度为2.
  • 节点的出度:从该节点出去的边数,例如下图结点1的出度为2.

有向图

 题目给的是无向图,无向图是一种特殊的有向图,每一条边都是双向的。

知识补充2:邻接表存储树/图
邻接表(本质是一个二维列表),为每个节点建立一个链表(list就可以)

每个节点对应的链表存储它可到达的相邻节点

邻接表存储有向图

 若是无向图,则邻接表为:

拓扑排序

本题可用DFS实现,也可用BFS实现
邻接表的形式存储这个多了一条边的树,同时记录每个节点的入度(用一个列表d)
所有入度是1的节点进行搜索,搜索过程中,它们搜索到的相邻节点入度减1,如果更新后的入度是1,继续搜索,否则结束搜索。

【图解BFS】:首先对所有入度是1的节点(下图橙色的点)进行搜索,搜索节点1和2后,节点5的入度为3-1-1=1。搜索节点3后,节点6的入度为,3-1=2,搜索节点4后,节点7的入度为3-1=2。继续搜索,发现入度为1的点只有节点5,搜索节点5后,节点8的入度为3-1=2。至此,没有入度为1的节点,结束搜索。发现没有搜索到的节点刚好连成一个环,就是题目要求的结果。

【图解DFS】 (红圈序号代表搜索顺序) 首先对节点1搜索,节点5的入度3-1=2,停止搜索;对节点2搜索,节点5入度2-1=1,继续搜索结点5,结点8的入度3-1=2,停止搜索;对节点3继续搜索,节点6的入度3-1=2,停止搜索;对节点4进行搜索,节点7的入度3-1=2,停止搜索。

  vis[ ]数组对所有搜索过的点打标记,最后统计,没有被标记过的节点一定在环上。(因为只是搜索入度为1的点,而环上的点入度至少为2,因此环上的点不会被搜索到)

代码1:BFS+拓扑排序 

from collections import deque
n = int(input())
g = [[] for _ in range(n + 1)]  # 邻接表(n+_1是因为第一行第一列用0占位,从下标1开始)
d = [0] * (n + 1)
vis = [0] * (n + 1)  # vis[ ]数组对所有搜索过的点打标记,最后统计,没有被标记过的节点一定在环上

def topsort():        # 拓扑排序
    q = deque() 
    for i in range(1, n + 1): # 遍历所有节点
        if d[i] == 1:   # 搜索入度为1
            q.append(i) # 入队
            vis[i] = 1  # 标记为搜索过
    while len(q):
        c = q.popleft() # 取出队头
        for i in g[c]:  # 搜索相邻节点
            d[i] -= 1   # 每次搜索,入度-1
            if d[i] == 1:    # 如果相邻节点入度为1
                q.append(i)  # 入队
                vis[i] = 1   # 标记为搜索过

for i in range(1, n + 1):
    a, b = map(int, input().split())    # 读入连接关系
    g[a].append(b)                      # 创建关系:列表a存入b
    g[b].append(a)                      # 创建关系:列表b存入a
    d[a] += 1                           # a的入度+1
    d[b] += 1                           # b的入度+1

topsort()
ring = []    # 用来存环
for i in range(1, n + 1):
    if not vis[i]:            # 找到没有标记过的
        ring.append(i)        # 加到环上
ring.sort()                   # 从小到大排序
print(*ring)                  # 将列表元素逐个输出

代码2:DFS+拓扑排序  

n = int(input())
g = [[] for _ in range(n + 1)]
d = [0] * (n + 1)
vis = [0] * (n + 1)

def dfs(c):
    vis[c] = 1                # 标记为搜索过
    for i in g[c]:            # 搜索相邻节点
        d[i] -= 1             # 入度-1
        if d[i] == 1:
            dfs(i)

def topsort():
    for i in range(1, n + 1):
        if d[i] == 1:        # 搜索入度为1的节点
            dfs(i)

for i in range(1, n + 1):
    a, b = map(int, input().split())
    g[a].append(b)
    g[b].append(a)
    d[a] += 1
    d[b] += 1

topsort()
ring = []
for i in range(1, n + 1):
    if not vis[i]:
        ring.append(i)
ring.sort()
print(*ring)

代码3:直接DFS (比较难想到)

N = int(input())
edge = [[] for i in range(N+1)]     #邻接表,0不用
pre = [0] * (N+1)                   #前驱点,用于生成环
ring = []                           #记录环
vis = [False] * (N+1)               #vis[i]=True表示这个点已经访问过
for i in range(N):
    u, v = map(int, input().split())
    edge[u].append(v)               #记录连接关系
    edge[v].append(u)
def dfs(x,fa):                      #x和它的父亲fa
    vis[x] = True                   #将当前节点标记为已访问
    for son in edge[x]:             #寻找连接的节点
        if len(ring) > 0: return    #这点已经确定在环上,不找了
        if not vis[son]:            #son未被标记,递归找
            pre[son] = x            #son的前驱记录为x
            dfs(son, x)             #son是从x找过来的
        elif son != fa:             #son已被标记,而且不是从x走过来的(当前节点存在两个入度)
            tmp = x                 #发现了环,son是环的终点,x是son的前继
            while tmp != son:       #不断向前寻找,向环中添加节点
                ring.append(tmp)
                tmp = pre[tmp]
            ring.append(son)        #把环的终点加入
dfs(1, 0)
ring.sort()                         #排序使其从小到大
for k in ring:   print(k, end=' ')


http://www.niftyadmin.cn/n/49905.html

相关文章

项目管理基础

项目的特点 项目是为提供独特产品、服务或成果所做的临时性努力 临时性(一次性,指项目有明确的开始时间和结束时间)独特性逐步完善-渐进明细资源约束目的性 项目的三重制约: 成本 质量 时间 其次还有范围 目标:多快…

【Axure教程】转盘抽奖原型模板

转盘抽奖是营销活动中很常用的一种方式,在线上我们也可以经常看到转盘抽奖的活动,所以今天作者就教大家在Axure中怎么制作一个转盘抽奖的原型模板。一、效果展示1、可以随机转动轮盘,轮盘停止时,指针对着的奖品高亮显示2、可以重复…

Lecture3 梯度下降(Gradient Descent)

目录 1 问题背景 2 批量梯度下降 (Batch Gradient Descent) 3 鞍点(Saddle Point) 3 随机梯度下降 (Stochastic Gradient Descent) 4 小批量梯度下降 (Mini-batch Gradient Descent) 1 问题背景 图1 上节课讲述的穷举法求最优权重值在Lecture2中,介绍了使用穷举…

vue2.0项目第一部分

论坛项目后端管理系统服务器地址:http://172.16.11.18:9090swagger地址:http://172.16.11.18:9090/doc.html前端h5地址:http://172.16.11.18:9099/h5/#/前端管理系统地址:http://172.16.11.18:9099/admin/#/搭建项目vue create . …

微信小程序 Springboot英语在线学习助手系统 uniapp

四六级助手系统用户端是基于微信小程序端,管理员端是基于web端,本系统是基于java编程语言,mysql数据库,idea开发工具, 系统分为用户和管理员两个角色,其中用户可以注册登陆小程序,查看英语四六级…

scipy稀疏数组dok_array

文章目录dok_array初始化方案内置方法dok_array dok数组就是通过键值对存储的数组,其中key就是矩阵中的坐标元组,value就是对应坐标中的值,是最容易理解的稀疏矩阵存储方案。 >>> import numpy as np >>> from scipy.spa…

化繁为简|中信建投基于StarRocks构建统一查询服务平台

近年来,在证券服务逐渐互联网化,以及券商牌照红利逐渐消退的行业背景下,中信建投不断加大对数字化的投入,尤其重视数据基础设施的建设,期望在客户服务、经营管理等多方面由经验依赖向数据驱动转变,从而提高…

第四章.误差反向传播法—简单层的实现(加法层(AddLayer)+乘法层(MulLayer))

第四章.误差反向传播法 4.1 简单层的实现 本章主要讲述的是加法层(AddLayer)和乘法层(MulLayer)的实现过程。 1.加法层(AddLayer) 从正向传播和反向传播两个层面进行分析。 1).示例: 以zxy为对象,观察它的传播。zxy的导数:∂z/∂x1,∂z/∂…