• uva 10972 RevolC FaeLoN


    双连通分量

    题意:一个无向图要添加多少条边才能使其变为边双连通分量,和  poj 3352 Road Construction 几乎一样的题目,不同的是,poj这题,原图是保证连通的,这题是不连通的,过程完全一样,只是最后计算答案的公式不同.所以题目分析就不写了,直接看poj那题吧,其实这题也是模板题,懂双连通分量的知识的话,并不需要看分析

    poj那题,缩点后不会出现孤立点,因为整个图连通的,所以只要找到缩点后的叶子个数就可以了,所以是(leaf+1)/2

    对于这题,因为图不连通,可能出现缩点后的孤立点。首先看缩点后的图,可能是一块一块的,对于点数超过1的块,和poj那题是一样的,只要找到叶子,所以没找到一个叶子就计数1,然后连接叶子,就能先把这块变成一个边双连通分量。对于那些只有一个点的块,要向其他块连至少2条边才能保证边双连通,所以找到缩点后有多少个孤立点,这些孤立点要计数2,表示连两条边

    最后答案就是(A + 1 + B*2)/2  , A表示有多少个叶子,B表示有多少个孤立点

    如果原图就是个边双连通分量,不需要加任何边,输出0

    同样给出两个代码,第1个代码是简化了tarjan

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <stack>
    #include <vector>
    using namespace std;
    #define N 1010
    
    int n,tot;
    int dfn[N],low[N],vis[N],de[N],dcnt,bcnt;
    vector<int>e[N];
    
    void init()
    {
        dcnt = bcnt = 0;
        for(int i=1; i<=n; i++)
        {
            dfn[i] = 0;
            e[i].clear();
            vis[i] = de[i] = 0;
        }
    }
    
    void dfs(int u ,int fa)
    {
        dfn[u] = low[u] = ++dcnt;
        vis[u] = 1;
        for(int i=0; i<e[u].size(); i++)
        {
            int v = e[u][i];
            if(v == fa) continue;
            if(!vis[v]) //树边
            {
                dfs(v,u);
                low[u] = min(low[u] , low[v]);
                if(low[v] > dfn[u]) //找到一个桥,新增一个连通分量
                    bcnt++;
            }
            else if(vis[v] == 1) //后向边
                low[u] = min(low[u] , dfn[v]);
        }
    }
    
    void solve()
    {
        for(int i=1; i<=n; i++)
            if(!vis[i])
            {
                dfs(i,-1);
                bcnt++;
            }
        if(bcnt == 1) //整个图本身就是个双连通分量
        { cout << 0 << endl; return ; }
    
        bool used[N];
        memset(used,false,sizeof(used));
        for(int i=1; i<=n; i++)
        {
            if(e[i].size() == 0) //孤立点,它自身形成一个连通分量
            { used[low[i]] = true ; continue; }
    
            for(int j=0; j<e[i].size(); j++)
            {
                int u = i;
                int v = e[i][j];
                used[low[u]] = used[low[v]] = true;
                if(low[u] != low[v]) //不同的连通分量
                    de[low[u]]++;
            }
        }
    
        int res = 0;
        for(int i=1; i<=n; i++)  //扫描缩点后每个点的度
            if(used[i] && de[i] == 0) //度为0即孤立点
                res += 2;
            else if(de[i] == 1) //叶子
                res++;
        cout << (res+1)/2 << endl;
    }
    
    int main()
    {
        while(cin >> n >> tot)
        {
            init();
            while(tot--)
            {
                int u,v;
                cin >> u >> v;
                e[u].push_back(v);
                e[v].push_back(u); 
                //偷懒直接用vector建图不用静态链表了
            }
            solve();
        }
        return 0;
    }

    模板,找到所有的桥

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <stack>
    #include <vector>
    #include <utility>
    using namespace std;
    #define N 1010
    
    int n,tot;
    int dfn[N],low[N],ins[N],belong[N],de[N],dcnt,bcnt;
    vector<int>e[N];
    stack<int>sta;
    typedef pair<int,int> pii;
    vector<pii>bridge;
    
    void init()
    {
        dcnt = bcnt = 0;
        while(!sta.empty()) sta.pop();
        bridge.clear();
        for(int i=1; i<=n; i++)
        {
            dfn[i] = 0;
            e[i].clear();
            ins[i] = de[i] = 0;
        }
    }
    
    void dfs(int u ,int fa)
    {
        sta.push(u); ins[u] = true;
        dfn[u] = low[u] = ++dcnt;
        for(int i=0; i<e[u].size(); i++)
        {
            int v = e[u][i];
            if(v == fa) continue;
            if(!dfn[v]) //树边
            {
                dfs(v,u);
                low[u] = min(low[u] , low[v]);
                if(low[v] > dfn[u]) //
                {
                    bridge.push_back(make_pair(u,v));
                    ++bcnt;
                    while(true)
                    {
                        int x = sta.top();
                        sta.pop(); ins[x] = 0;
                        belong[x] = bcnt;
                        if(x == v) break;
                    }
                }
            }
            else if(ins[v]) //后向边
                low[u] = min(low[u] , dfn[v]);
        }
    }
    
    void solve()
    {
        for(int i=1; i<=n; i++)
            if(!dfn[i])
            {
                dfs(i,-1);
                bcnt++;
                while(!sta.empty())
                {
                    int x = sta.top();
                    sta.pop(); ins[x] = 0;
                    belong[x] = bcnt;
                    if(x == i) break;
                }
            }
        if(bcnt == 1)
        {cout << 0 << endl; return ;}
        
        for(int i=0; i<bridge.size(); i++) //取出所有的桥
        {
            int u = bridge[i].first;
            int v = bridge[i].second;
            de[belong[u]]++;
            de[belong[v]]++;
        }
        int res = 0;
        for(int i=1; i<=bcnt; i++)
            if(de[i] == 0)      res += 2;
            else if(de[i] == 1) res++;
    
        cout << (res+1)/2 << endl;
    }
    
    int main()
    {
        while(cin >> n >> tot)
        {
            init();
            while(tot--)
            {
                int u,v;
                cin >> u >> v;
                e[u].push_back(v);
                e[v].push_back(u);
            }
            solve();
        }
        return 0;
    }
  • 相关阅读:
    Java小细节
    LinkedHashMap的accessOrder的作用
    异或运算及其应用
    什么是FullStack设计
    Java正则表达式收藏
    offsetLeft,Left,clientLeft的区别
    java文件常用操作(2) 从文件末尾开始读取文件
    java文件常用操作
    各种排序方法
    2013微软暑期实习笔试&面试总结
  • 原文地址:https://www.cnblogs.com/scau20110726/p/3086330.html
Copyright © 2020-2023  润新知