POJ 3310(并查集判无环连通图)

题意:给定一个无向图,判断是否是一个caterpillar。是caterpillar的条件是:一,无环连通图。二,存在一条'脊柱',使所有的点要么在‘脊柱’上,要么与在‘脊柱’上的点相连。

思路:可用并查集判断是否是无环连通图,若有环则无法成功合并两个点,若非连通图则最后所有点的父节点不会是同一个。接着判断是否含有“脊柱”,只需要将每个分支点中与之相连的度为1的点删除掉,全部删掉之后,看剩下的点的度是否小于等于2即可,如果某点的度大于2,则说明它至少连了两个度不为1的分支,则这必定不是一个“脊柱”。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<vector>
#include<cmath>
#include<set>
using namespace std;
const int maxn = 1e9;
const int maxt = 1e8;
const double eps = 1e-6;
typedef long long ll;
const ll MOD = 1e9;
const int inf = 0x3f3f3f3f3f;
set<int> s[110];
int d[110][110];
int f[110];
int tmp_len[110];
int n, e;
int Find(int x)//寻找点x的父节点
{
    return x == f[x] ? x : f[x] = Find(f[x]);
}
bool Union(int x, int y)//合并两点
{
    int fx = Find(x);
    int fy = Find(y);
    if(fx == fy) return false;//有环
    if(fx < fy)//使其父节点相同。若为联通图则最后所有点的父节点应该是同一个
        f[fy] = fx;//有个地方没太弄懂,如果不分大小情况的话最后所有点的父节点是不相同的,只有这样分了大小以后联通图所有点的父节点才相同
    else
        f[fx] = fy;
    return true;
}
bool judge()//判断是否含有”脊柱“
{
    set<int> :: iterator it;
    for(int i = 1; i <= n; i++)
    {
        int len = s[i].size();
        if(len > 1)//找到度大于1的点,即含有分支的点
        {
            tmp_len[i] = len;//记录点i当前的度数顺便标记是否访问过该点(访问过,才会给其赋值)
            for(it = s[i].begin(); it != s[i].end(); it++)//遍历与该点相连的所有的点
            {
                int u = *it;
                if(tmp_len[u]) continue;//访问过该点,跳过
                if(s[u].size() == 1)//删除与之相连的度为1的点
                {
                    tmp_len[i]--;//点i的度数减一
                    d[i][u] = d[u][i] = 0;//u i 两点不再相连
                }
            }
        }
    }
    bool ok = true;
    for(int i = 1; i <= n; i++)
    {
        if(tmp_len[i] > 2)//若经过上面的删除操作后还有度大于2的点,则不是”脊柱“
        {
            ok = false;
            break;
        }
    }
    return ok;
}
int main()
{
    int kase = 0;
    while(~scanf("%d",&n) && n)
    {
        memset(f, 0, sizeof(f));//记录每个点的父节点
        memset(d, 0, sizeof(d));//标记u v两点是否相连,相连为1,否则为0
        memset(tmp_len, 0, sizeof(tmp_len));
        for(int i = 0; i <= n; i++)
            s[i].clear();
        for(int i = 1; i <= n; i++)
            f[i] = i;//初始时所有点的父节点使其本身
        scanf("%d",&e);
        int u, v;
        for(int i = 0; i < e; i++)
        {
            scanf("%d%d",&u,&v);
            s[u].insert(v);
            s[v].insert(u);
            d[u][v] = 1;//标记u v 两点相连
        }
        bool ok = true;
        for(int i = 1; i <= n; i++)
        {
            for(int j = 1; j <= n; j++)
            {
               if(d[i][j] && !Union(i, j))//若U V 两点相连并且可以合并,则说明该图为无环图
               {
                 ok = false;
                 break;
               }
            }
        }
        if(ok == true)//判断是否是连通图,即所有点的父节点是否相同
        {
            for(int i = 2; i <= n; i++)
                if(f[i] != f[i - 1])
                {
                    ok = false;
                    break;
                }

        }
        if(!ok)
        {
            printf("Graph %d is not a caterpillar.\n",++kase);
            continue;
        }
        if(judge())
            printf("Graph %d is a caterpillar.\n",++kase);
        else
            printf("Graph %d is not a caterpillar.\n",++kase);

    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值