zoukankan      html  css  js  c++  java
  • 【并查集之判断连通无环图】

    HDU-1272 | HDU-1325 小希的迷宫 | Is it a tree ?

    http://acm.hdu.edu.cn/showproblem.php?pid=1272

    http://acm.hdu.edu.cn/showproblem.php?pid=1325

    判断一张图是否是一颗树(连通,无环)的两个关键点:

    1. 不存在环路(对于有向图,不存在环路也就意味着不存在强连通子图)
    2. 满足边数加一等于顶点数的规律(不考虑重边和指向自身的边)——根唯一

    其实我觉得这两个题目差不多啊,只不过1325把边换成了有向边,我觉得ok啊,没什么差别把其实是不同的,比如1 - 2, 2 - 3,4 - 2这样你如果不加任何优化结果是以3为根得树了(连老大),很明显跟不是聚来的,而是发散的,就是根得入度是0,所以1325加上一个入度得判断就行了(而且常见并查集和入度出度放一块啊)还有欧拉回路问题,以后都会去回顾一下

    先来个简单的1272

    #include <iostream>
    #include <string.h>
    #include <cstdio>
    using namespace std;
    const int maxn = 1e5 + 1e4;
    int pre[maxn],s[maxn],vis[maxn];
    int pcnt,ecnt,retflag;
    
    void init()
    {
        for(int i = 0;i < maxn;i++)
        {
            pre[i] = i;
            s[i] = 1;
            vis[i] = 0;
        }
        pcnt = ecnt = 0;
        retflag = 1;
    }
    int Find(int x)
    {
        while(x != pre[x])
        {
            pre[x] = pre[pre[x]];
            x = pre[x];
        }
        return x;
    }
    void join(int a,int b)
    {
        int u = Find(a);
        int v = Find(b);
        if(u == v)
        {
            retflag = 0;
        }
        else
        {
            if(!vis[a] && u == a)vis[a] = 1,pcnt++;
            if(!vis[b] && v == b)vis[b] = 1,pcnt++;
            ecnt++;
            if(s[u] > s[v])
            {
                pre[v] = u;
                s[u] += s[v];
            }
            else
            {
                pre[u] = v;
                s[v] += s[u];
            }
        }
    }
    int main()
    {
        int a,b;
        init();
        while(~scanf("%d%d",&a,&b))
        {
            if(a == b && a == -1)break;
    
            if(a == b && a == 0)
            {
                if(ecnt == 0)printf("Yes
    ");
                else if(!retflag || ecnt + 1 != pcnt)
                {
                    printf("No
    ");
                }
                else printf("Yes
    ");
                init();
            }
            else if(!retflag)
            {
                continue;
            }
            else
            {
                join(a,b);
            }
        }
        return 0;
    }
    

     基本上大部分都是模板得嵌套,所以就不分开讲了,虽然有路径压缩和平衡树得保持,但是时间仍然很慢,因为我不知道数据得范围,所以每次更新次数非常多GG,1325就会改变一些了(然后点的个数和边的个数我都一块放在里面判断了,觉得拿出来太费时间)

    1325

    #include <iostream>
    #include <string.h>
    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    using namespace std;
    const int maxn = 1e6;
    int pre[maxn],vis[maxn],in[maxn];
    int pcnt,ecnt,retflag;
    
    void init(int n = 1e5 + 1e4)
    {
        for(int i = 0;i <= n;i++)
        {
            pre[i] = i;
            vis[i] = 0;
            in[i] = 0;
        }
        pcnt = ecnt = 0;
        retflag = 1;
    }
    int Find(int x)
    {
        while(x != pre[x])
        {
            pre[x] = pre[pre[x]];
            x = pre[x];
        }
        return x;
    }
    void join(int a,int b)
    {
        int u = Find(a);
        int v = Find(b);
        if(u == v)
        {
            if(!vis[u])vis[u] = 1,pcnt++;
            retflag = 0;
        }
        else
        {
            if(!vis[a] && u == a)vis[a] = 1,pcnt++;
            if(!vis[b] && v == b)vis[b] = 1,pcnt++;
            ecnt++;
            if(++in[b] > 1)retflag = 0;
            pre[u] = v;
        }
    }
    int main()
    {
        int a,b,cas = 1;
        int retn = 0;
        init();
        while(~scanf("%d%d",&a,&b))
        {
            if(a < 0 || b < 0)break;
            retn = max(retn,max(a,b));
            if(a == b && a == 0)
            {
                if(ecnt == 0 && pcnt == 0)printf("Case %d is a tree.
    ",cas++);
                else if(!retflag || ecnt + 1 != pcnt)
                {
                    printf("Case %d is not a tree.
    ",cas++);
                }
                else printf("Case %d is a tree.
    ",cas++);
                init(retn);
            }
            else if(!retflag)
            {
                continue;
            }
            else
            {
                join(a,b);
            }
        }
        return 0;
    }
    

     1325这个题,有一个坑人得地方就是结束得判断是a或b有一个是负数,我说为什么老是re,原来访问了a[-2]……

    其他的都比较好说

  • 相关阅读:
    hdu 2082 找单词
    hdu 2079 选课时间(题目已修改,注意读题)
    hdu 2073 无限的路
    hdu 2062 Subset sequence
    poj 2777 Count Color
    hdu 2067 小兔的棋盘
    卡特兰数
    poj3468
    hdu1394
    hdu1166(线段树之单点更新)
  • 原文地址:https://www.cnblogs.com/DF-yimeng/p/8611751.html
Copyright © 2011-2022 走看看