哈理工OJ 1959 森林木(并查集)

本文介绍了一个算法问题,通过输入顶点和边来判断给定的图结构是否为树、森林或一般图。使用并查集的方法解决该问题,并提供了完整的C++代码实现。

森林木
Time Limit: 1000 MSMemory Limit: 32768 K
Total Submit: 85(36 users)Total Accepted: 38(32 users)Rating: Special Judge: No
Description

什么是一棵树?Something like A.

什么是一片森林?Something like B.

如果你依旧不理解, please go to baidu.


        

Input

本题有多组测试数据,每组数据第一行输入两个整数n和m,n代表有多少个点,m代表有m条无向边,接下来m行分别是m条边,题目保证无重边,无自环(即a走到a)。

(1≤n≤10000,1≤m≤1000000)

Output

对于每组测试数据输出结果,如果是一棵树,请输出“This is a tree.”,如果是一片森林,请输出“This is a forest.”,如果既不是森林也不是树,请输出“This is a graph.”。每组输出占一行。

Sample Input
3 2
1 2
2 3
4 4
1 2
2 3
3 1
1 4
Sample Output
This is a tree.
This is a graph.
Source
"科林明伦杯"哈尔滨理工大学第三届ACM程序设计团队预选赛

这是我们学长某一届的校团队赛的预选赛。思路还是很好建立的,如果对于大神来说这就是一个拼手速的题目吧~。

这里我们知道,如果自环了,那么就什么也不是,否则如果有一个父节点就是一棵树,如果大于一个父节点,那么说明就是森林了~

 #include<stdio.h>
#include<string.h>
using namespace std;
int flag;
int f[12121212];
void init(int n)
{
    for(int i=1;i<=n;i++)
    f[i]=i;
    flag=0;
}
int find(int x)
{
    return f[x] == x ? x : (f[x] = find(f[x]));
}
void merge(int a,int b)
{
    int A,B;
    A=find(a);
    B=find(b);
    if(A!=B)
    f[B]=A;
    else flag=1;
}
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        init(n);
        for(int i=0;i<m;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            merge(x,y);
        }
        if(flag==1)
        printf("This is a graph.\n");
        else
        {
            int cont=0;
            for(int i=1;i<=n;i++)
            {
                if(f[i]==i)
                cont++;
            }
            if(cont==1)
            {
                printf("This is a tree.\n");
            }
            else
            {
                printf("This is a forest.\n");
            }
        }
    }
}









### 使用并查集算法检测图中是否存在环 #### 并查集简介 并查集是一种型的数据结构,用于处理一些不相交集合的合并及查询问题。基本操作包括`union`(合并) 和 `find`(查找),这些操作能够高效地管理动态连通性问题。 #### 实现思路 当尝试构建一棵生成时,每加入一条新边 `(u, v)` ,先检查这两个顶点是否已经在同一个集合内。如果是,则意味着形成了一个新的回路;如果不是,则将两个顶点所在的集合合并成一个更大的集合[^2]。 #### 示例代码 下面是一个简单的 Python 函数,它接收一个由多个元组组成的列表作为输入参数,其中每个元祖代表了一条连接两个节点之间的无向边,并返回布尔值指示该图是否有环: ```python class UnionFind: def __init__(self, size): self.parent = list(range(size)) self.rank = [0] * size def find(self, p): if self.parent[p] != p: self.parent[p] = self.find(self.parent[p]) # Path compression return self.parent[p] def union(self, p, q): rootP = self.find(p) rootQ = self.find(q) if rootP == rootQ: return False if self.rank[rootP] > self.rank[rootQ]: self.parent[rootQ] = rootP elif self.rank[rootP] < self.rank[rootQ]: self.parent[rootP] = rootQ else: self.parent[rootQ] = rootP self.rank[rootP] += 1 return True def has_cycle(edges, n_nodes): uf = UnionFind(n_nodes) for u, v in edges: if not uf.union(u - 1, v - 1): # Adjusting index from 1-based to 0-based return True return False ``` 此函数首先初始化了一个大小为给定节点数量的并查集实例。接着迭代遍历所有的边,在每次调用`uf.union()`之前都会自动完成路径压缩和按秩合并的操作以保持较低的时间复杂度。一旦发现某次联合失败(即两点已经属于同一集合),就立即返回True表明存在至少一个环;否则最终返回False表示不存在任何环。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值