蓝桥杯·发现环+蓝桥侦探 Python题解(拓扑排序+并查集)

本文探讨了如何使用拓扑排序和并查集解决计算机网络中的环路问题,以及在蓝桥侦探任务中如何利用这些算法确定说谎者。通过实例解析和两种方法的对比,展示了图论技术在解决实际问题中的关键作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

题目一:发现环

题解1.1:拓扑排序

题解1.2:并查集 +DFS

题目二:蓝桥侦探

题解2.1:拓扑排序

题解2.2:并查集 


题目一:发现环

题目描述

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

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

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

输入描述

输入范围:

第一行包含一个整数 N 。

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

其中,1≤N≤105,1≤a,b≤N。

输入保证合法。

输出描述

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

输入输出样例

输入:

5
1 2
3 1
2 4
2 5
5 3

输出:

1 2 3 5

题解1.1:拓扑排序

        拓扑排序用于来找出图中的线性序列。在排序过程中倘若到最后还有节点未处理,则说明该图中存在环。我们利用这一性质来找出题中的环。

        要注意常规的拓扑排序一般用于有向无环图中,入队条件是节点入度为0,再寻找它指向的节点。而此题将条件修改为度数=1,不断地寻找与其相连的节点,最后剩下的节点度数不为1,则一定在环中。

from collections import defaultdict,deque
d=defaultdict(int)                            #度数
used=defaultdict(int)                         #是否已排序
tree=defaultdict(list)
n=int(input())

for i in range(n):
    u,v=map(int,input().split())
    tree[u].append(v)
    tree[v].append(u)
    d[u]+=1
    d[v]+=1

a=[]
for i,j in d.items():
    if j==1:                                    #找出度数为1的节点
        a.append(i)
        used[i]=1

q=deque(a)
while q:
    u=q.popleft()
    for v in tree[u]:
        if not used[v]:
            d[v]-=1                            #从图中去掉u节点            
            if d[v]==1:                        
                q.append(v)
                used[v]=1

res=[]
for i,j in d.items():
    if j!=1:
        res.append(i)
res.sort()
print(" ".join(map(str,res)))

题解1.2:并查集 +DFS

       如果两个点的父节点是一样的,说明这两个点一定在环上,用其中一个当起点,另一个当终点,DFS搜索环中所有点。

from collections import defaultdict
n=int(input())
p=[i for i in range(n+1)]
used=[0]*(n+1)
tree=defaultdict(list)
ans=[0]*n

def find(x):
  if p[x]!=x:
    p[x]=find(p[x])
  return p[x]

def dfs(pos,idx):
  ans[idx]=pos
  if pos==end:
    res=sorted(ans[:idx+1])
    print(" ".join(map(str,res)))
    return
  for v in tree[pos]:
    if not used[v]:
      used[v]=1                        #注意回溯
      dfs(v,idx+1)
      used[v]=0

start=0
for i in range(n):
  x,y=map(int,input().split())
  tx,ty=find(x),find(y)
  if tx==ty:
    start,end=x,y
    break
  else:
    p[ty]=tx
    tree[x].append(y)
    tree[y].append(x)
    
used[start]=1
dfs(start,0)

题目二:蓝桥侦探

题目描述

小明是蓝桥王国的侦探。

这天,他接收到一个任务,任务的名字叫分辨是非,具体如下:

蓝桥皇宫的国宝被人偷了,犯罪嫌疑人锁定在 N 个大臣之中,他们的编号分别为 1∼N。

在案发时这 N 个大臣要么在大厅1,要么在大厅2,但具体在哪个大厅他们也不记得了。

审讯完他们之后,小明把他们的提供的信息按顺序记了下来,一共 M 条,形式如下:

  • x y,表示大臣 x 提供的信息,信息内容为:案发时他和大臣 y 不在一个大厅。

小明喜欢按顺序读信息,他会根据信息内容尽可能对案发时大臣的位置进行编排。

他推理得出第一个与先前信息产生矛盾的信息提出者就是偷窃者,但推理的过程已经耗费了他全部的脑力,他筋疲力尽的睡了过去。作为他的侦探助手,请你帮助他找出偷窃者!

输入描述

第 1 行包含两个正整数 N,M,分别表示大臣的数量和口供的数量。

之后的第 2 ∼M+1 行每行输入两个整数 x , y,表示口供的信息。

1≤N,M≤5×10^5,1≤x,y≤N

输出描述

输出仅一行,包含一个正整数,表示偷窃者的编号。

输入输出样例

输入:

4 5 
1 2
1 3 
2 3 
3 4
1 4

输出: 

2

 题解2.1:拓扑排序

        我们可以把(x,y)看成是图的一条边,这样每添加一条边就检查是否形成了环(找环思路参照1.1)。如果形成了环,那么这个人就是说谎的人。

        思路大致如上,但实操大概率超时,有精力的读者请自行验证。

题解2.2:并查集 

        这里用到的并不是一般的并查集(只能维护“朋友的朋友是朋友”的关系),而是种类并查集(“敌人的敌人也是朋友”)

        对于一个个体a,假设存在与a对立的个体!a,如果b与a对立,那么b与!a在同一并查集(朋友),a与!b也在同一并查集;反之如果b与a是朋友,那么b与!a不在同一并查集(对立),即a与b在同一并查集,!a与!b在同一并查集

n,m=map(int,input().split())
ans=0
p=[x for x in range(2*n)]        #开辟一个2*n的空间,分为x和x+n(y和y+n)

def find(x):
  if p[x]!=x:
    p[x]=find(p[x])
  return p[x]

def union(x,y):
  tx,ty=find(x),find(y)
  if tx!=ty:
    p[tx]=ty

for i in range(m):
  x,y=map(int,input().split())
  if ans!=0:  break
  tx,ty=find(x),find(y)
  txn,tyn=find(x+n),find(y+n)
  if tx==ty or txn==tyn:
    ans=x
  else:
    union(tx,tyn)
    union(ty,txn)

print(ans)

若有不正确之处,烦请在评论区指出! 

### 关于蓝桥杯 C++ 编程题目及其解法 #### 题目背景与分析 蓝桥杯竞赛中的编程问题通常涉及算法设计、数据结构应用以及优化技巧。以下是一些常见的题型解析: 1. **树形动态规划 (Tree DP)** 给定一棵树,要求通过某种方式计算节点的属性值。例如,在引用[1]中提到的一个经典问题是关于子树的最大贡献值计算[^1]。此问题可以通过深度优先搜索(DFS)实现,并利用数组 `f` 来存储每个节点的结果。 2. **多重计数校正** 当涉及到多个集合之间的交集运算时,可能会遇到重复计数的情况。引用[2]描述了一个典型的例子:当某些条件成立时,需要调整最终结果以消除多余或不足的部分[^2]。这种类型的题目常见于组合数学领域。 3. **简单输入输出操作** 对于初学者来说,掌握基本的输入输出方法非常重要。比如引用[3]展示了一个简单的加法程序,它接收两个整数作为输入并打印它们的和[^3]。虽然看似基础,但对于熟悉语法很有帮助。 4. **经济模型模拟** 参考引用[4]提到了一个有关资源分配的问题——如何最小化花费来满足特定需求?这类问题可能需要用到贪心策略或者线性规划等高级技术[^4]。 #### 示例代码片段 以下是针对上述几种情况所提供的部分解决方案示例: ```cpp // 树形DP模板 #include <bits/stdc++.h> using namespace std; long long n, x, f[100005]; vector<long long> v[100005]; void dfs(long long node){ for(auto child : v[node]){ dfs(child); f[node] = max(f[node], f[child]); } f[node] += v[node].size(); } int main(){ cin >> n; for(int i=2;i<=n;i++){ cin >> x; v[x].push_back(i); } dfs(1); cout << f[1] << endl; } ``` ```cpp // 处理多重计数修正逻辑 #include <iostream> using namespace std; int A[100005], B[100005], C[100005]; long long res = 0; int main() { int N; cin >> N; for(int i=1;i<=N;i++) cin>>A[i]>>B[i]>>C[i]; // 计算初步结果 for(int i=1;i<=N;i++) for(int j=1;j<=N;j++) for(int k=1;k<=N;k++) res += A[i]*B[j]*C[k]; // 扣除重复项 for(int i=1;i<=N;i++) { res -= A[i]*B[i]*(B[i]-1)/2 * C[i]; // Case: i == j res -= A[i]*C[i]*(C[i]-1)/2 * B[i]; // Case: k == l res -= A[i]*C[i]*B[i]; // Case: i == l && k == j res += A[i]*(A[i]-1)*(B[i]+C[i])/2; // Special case adjustment when all indices are equal. } cout<<res*(res-1)<<endl; } ``` ```python # 基础IO练习 a, b = map(int, input().split()) print(a + b) ``` #### 总结说明 以上内容涵盖了从入门到进阶的不同层次挑战项目。无论是构建复杂的递归函数还是解决实际生活场景下的预算管理难题,都能找到相应的理论依据和技术支持。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Eureka!!!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值