4216 图中的环(基环树)

1. 问题描述:

给定一个 n 个点 m 条边的无向图。点的编号从 1 到 n。图中不含重边和自环。请你对给定图进行判断,如果该图是一个有且仅有一个环的连通图,则输出 YES,否则输出 NO。

输入格式

第一行包含两个整数 n,m。接下来 m 行,每行包含两个整数 a,b,表示点 a 和点 b 之间存在一条无向边。

输出格式

如果该图是一个有且仅有一个环的连通图,则输出 YES,否则输出 NO。

数据范围

前三个测试点满足 1 ≤ n ≤ 10。
所有测试点满足 1 ≤ n ≤ 100,0 ≤ m ≤ n(n − 1) / 2,1 ≤ a,b ≤ n。

输入样例1:

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

输出样例1:

YES

输入样例2:

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

输出样例2:

NO
来源:https://www.acwing.com/problem/content/description/4219/

2. 思路分析:

首先我们需要分析一下满足题目要求的图有什么性质,可以发现首先需要连通,连通那么就一定存在一棵生成树,因为只有一个环所以这种图是环上挂着一堆树,由这个特点可以知道满足题目要求的这种图属于基环树,然后我们看一下基环树有什么特点,可以发现当一棵树是基环树的时候有两个特点:① 连通图;② m = n;并且当图满足特点①②的时候那么这种图属于基环树,所以这两个条件属于充分必要条件,因为当图连通的时候一定存在一棵生成树,而不管我们在生成树哪两个节点添加一条边最终都只有一个环,所以m = n,所以满足这两个特点的图是基环树;我们可以判断m是否等于n,如果m != n说明一定不是基环图,然后判断所有点是否在同一个集合中,判断所有点是否在同一个集合可以使用并查集进行判断:

3. 代码如下:

from typing import List


class Solution:
    # 并查集的find操作, 查找节点x的父节点, 查找节点的时候进行路径压缩
    def find(self, x: int, p: List[int]):
        if p[x] != x: p[x] = self.find(p[x], p)
        return p[x]

    def process(self):
        n, m = map(int, input().split())
        # n != m的时候说明不是基环图
        if n != m:
            print("NO")
            return
        # 使用并查集判断是否连通, p为并查集
        p = [i for i in range(n + 10)]
        res = n
        for i in range(m):
            a, b = map(int, input().split())
            fa, fb = self.find(a, p), self.find(b, p)
            # 当前两个点不在同一个集合那么合并这两个点
            if fa != fb:
                p[fa] = fb
                res -= 1
        # 说明所有点不是在同一个集合
        if res == 1:
            print("YES")
            return
        else:
            print("NO")
            return


if __name__ == '__main__':
    Solution().process()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要找出基环树c,可以使用深度优先搜索(DFS)算法来实现。以下是使用vector存储的一个示例代码: ```cpp #include <iostream> #include <vector> using namespace std; vector<bool> visited; // 记录节点是否已经访问过 vector<int> parent; // 记录每个节点的父节点 vector<bool> inCycle; // 记录节点是否在中 vector<int> cycle; // 存储找到的 // 深度优先搜索 bool dfs(vector<vector<int>>& graph, int node, int parentNode) { visited[node] = true; parent[node] = parentNode; for (int neighbor : graph[node]) { if (!visited[neighbor]) { if (dfs(graph, neighbor, node)) return true; } else if (neighbor != parentNode) { // 找到了 cycle.push_back(neighbor); inCycle[neighbor] = true; int cur = node; while (cur != neighbor) { cycle.push_back(cur); inCycle[cur] = true; cur = parent[cur]; } cycle.push_back(neighbor); // 将最后一个节点加入中 return true; } } return false; } // 找到基环树c vector<int> findCycle(vector<vector<int>>& graph) { int n = graph.size(); visited.resize(n, false); parent.resize(n, -1); inCycle.resize(n, false); for (int i = 0; i < n; i++) { if (!visited[i] && dfs(graph, i, -1)) break; } return cycle; } int main() { int n = 4; // 的节点数 vector<vector<int>> graph(n); // 添加的边 graph[0].push_back(1); graph[1].push_back(0); graph[1].push_back(2); graph[2].push_back(1); graph[2].push_back(3); graph[3].push_back(2); vector<int> cycle = findCycle(graph); // 输出c中的节点 cout << "c中的节点: "; for (int node : cycle) { cout << node << " "; } cout << endl; return 0; } ``` 以上代码通过深度优先搜索遍历,当遇到一个已经访问过的节点时,如果该节点不是当前节点的父节点,则找到了一个。然后通过记录每个节点的父节点,可以回溯找到构成该的所有节点。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值