zoukankan      html  css  js  c++  java
  • hdu 3671 Boonie and Clyde

    双连通分量

    题意:给一个无向图,要求毁掉两个点,使图变得不连通,图一开始是连通的

    因为要毁掉两个点,就不是简单的求割点,再看看数据范围,点数为1000,边数为10000,Tarjan的时间复杂度为O(E),如果用枚举法,先枚举要毁掉的第一个点,再用Tarjan进行处理来找割点会不会超时呢?答案是不会,时间为O(v*E),刚好是千万级别,不超

    做法:先枚举要删除的第1个点,在原图中删除它,看看删除它后整个图的变化

            1.整个图变得不连通了(即这个点本身是割点),但是还没完要分类讨论一下

            (1).整个图变为两部分,但是两部分刚好都是一个点,那么这两个点再毁掉哪个点都好,图的连通分支数都不会增加,这是一个特殊情况

          例如,(1,2)(2,3)这种图,是无解的,任意毁掉两个点都无法增加图的连通分支,所以方案数为0

            (2).整个图分为两部分,但是有一部分的点数为1,另一部分大于1,那么这时候只要在较大的那部分,任意毁掉一个点(无论是不是割点都行),最后整个图都会至少被分为了两个部分

                    (如果毁掉的是割点,将分成更多份),所以这样产生的方案数是V-2

            (3).整个图分为了两个部分,两个部分的点数都大于1,那么任意在哪个部分毁掉那个点都可以(无论是不是割点都行),最后整个图都会至少分为两个部分,所以方案数为V-1

            (4).整个图被分为了三个或更多的部分,那么也是在剩下的点中任意毁掉一个点都可以(无论那个点是不是割点),方案数为V-1

                  (如果这个点刚好处于一个部分且这个部分只有它自己一个点,那么    毁掉后整个图的分支数减1;如果这个点在一个部分且这个部分不止它一个点且这个点不是割点,那么分支数  不会增加,如果是割点分支数为增加)

         2.删除第一个点后,整个图还是连通的(是连通,不是双连通)

            那么就在剩下的图中找割点,找到几个,方案数就是多少

    最后注意一点,这样计算的结果,很容易想到是有重复的,但是不难想到,其实刚好重复了一次,因为对于一个图,方案是固定的,枚举了所有点,找出了所有已方案,相当于每个方案算了两次,最后答案除2即可

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    #define N 1010
    #define M 10010
    
    int n,tot,cancel,rts,num;
    int e[N][M];
    int dfn[N],low[N],cut[N],iscut[N],cnum,dcnt;
    
    void dfs(int u ,int fa)
    {
        dfn[u] = low[u] = ++dcnt;
        ++num;
        for(int i=1; i<=e[u][0]; i++)
        {
            int v = e[u][i];
            if(v == cancel || v == fa) continue;
            if(!dfn[v])
            {
                dfs(v,u);
                low[u] = min(low[u] , low[v]);
                if(fa == -1) {rts++; continue;}
                if(dfn[u] <= low[v])
                {
                    if(!iscut[u])
                    { iscut[u] = 1; cut[cnum++] = u;}
                }
            } 
            else low[u] = min(low[u] , dfn[v]);
        }
    }
    
    void solve()
    {
        int res = 0,count,ok;
        for(cancel = 1; cancel<=n; cancel++) //枚举第1个删除的点
        {
            count = cnum = 0;
            memset(dfn,0,sizeof(dfn));
            memset(low,0,sizeof(low));
            memset(iscut,0,sizeof(iscut));
            ok = 0;
            for(int i=1; i<=n; i++)
                if(i!=cancel && !dfn[i])
                {
                    count++;
                    num = rts = 0;
                    dfs(i , -1);
                    if(num == 1) { ok++; continue; }
                    if(rts >= 2) { iscut[i] = 1; cut[cnum++] = i; }
                }
            if(count >= 3) res += (n-1);
            else if(count == 2 && ok == 0)   res += (n-1);
            else if(count == 2 && ok == 1)   res += (n-2);
            else if(count == 1) res += cnum;
        }
        cout << res/2 << endl;
    }
    
    int main()
    {
        int cas = 0;
        while(cin >> n >> tot)
        {
            if(!n && !tot) break;
            for(int i=1; i<=n; i++)
                e[i][0] = 0;
            while(tot--)
            {
                int u , v , c;
                cin >> u >> v;
                c = ++e[u][0];
                e[u][c] = v;
                c = ++e[v][0];
                e[v][c] = u;
            }
            printf("Case %d: ",++cas);
            solve();
        }
        return 0;
    }
  • 相关阅读:
    26个精选的JavaScript面试问题
    用js实现随机选取10–100之间的10个数字,存入一个数组,并排序
    小程序布局中class='container'的bug
    PHP接收数据数据包的几个方式
    LINUX命令
    VMware的下载安装
    php中Sessions
    php中Cookies
    php文件上传
    php文件处理
  • 原文地址:https://www.cnblogs.com/scau20110726/p/3092078.html
Copyright © 2011-2022 走看看