• POJ 3177 Redundant Paths(重边标记法,有重边的边双连通分支)


    大致题意:

           为了保护放牧环境,避免牲畜过度啃咬同一个地方的草皮,牧场主决定利用不断迁移牲畜进行喂养的方法去保护牧草。然而牲畜在迁移过程中也会啃食路上的牧草,所以如果每次迁移都用同一条道路,那么该条道路同样会被啃咬过度而遭受破坏。

           现在牧场主拥有F个农场,已知这些农场至少有一条路径连接起来(不一定是直接相连),但从某些农场去另外一些农场,至少有一条路可通行。为了保护道路上的牧草,农场主希望再建造若干条道路,使得每次迁移牲畜时,至少有2种迁移途径,避免重复走上次迁移的道路。已知当前有的R条道路,问农场主至少要新建造几条道路,才能满足要求?

     
    错误做题思路: 求出桥的个数n,也应该考虑重边的情况. 结果应该是 (n + 1) / 2;
    但是这样做是错误的 仅仅考虑桥是不可行的
    比如一个最简单的图    1-> 2 -> 3 ->4  根据我们的上述结果我们得到 3 个桥,也就是说需要加 两条边就行了, 其实 我们只要将 1 - 4相连就行了。
    因此我们上面的结论是错误的。
    (思考问题要细心要仔细要全面)
     
    正确思路:  要先将图 强联通分量缩点, 在无向图中我们称为边双连通分量。 将所有边双连通分量求出来缩成点,就形成了一棵树,我们只要判断树的叶子结点的个数就行了。
                        假设叶子节点的个数是 n  那么 就有 (n+1)/2 条边就能将这个图变成没有桥的 双连通图
                        判断一个点是否是叶子节点 只要判断这个点的度就行了,度为 1 的点就是叶子节点。
     
     
    知识汇总:
    求桥:
    在求割点的基础上吗,假如一个边没有重边(重边 1-2, 1->2 有两次,那么 1->2 就是有两条边了,那么 1->2就不算是桥了)。
    当且仅当 (u,v) 为父子边,且满足 dfn[u] < low[v]
    我们求一个边双连通分支,在网上有人说在进行完 Tarjan  之后  根据 low 的值 直接划分边双分支,其实这个是不可行的,下面是反例
     
    数据 
    4 5
    1 2
    1 3
    2 3
    2 4
    3 4
     
    根据上图我们可以看出来, 他们是属于一个边双连通的分支,但是呢 low值 却是不一样的。
     
    这里我们求边双连通分支是和求强联通分支是一样的,但是还有一点区别,其实我们没有必要再标记这个点是否在栈中。
    因为我们的边是双向的, 单向的无法确定 u->v   v一定能到 u ? 
     
    标价重边:
    在这里使用了一个标志变量 K,  假如这个点到父亲的路有两条, 那么我 第一条可以不走,但是第二条必须要走,因为是重边,这样我的儿子节点也就可以更新 low 了。
     
    #include <iostream>
    #include <cstdlib>
    #include <cstdio>
    #include <algorithm>
    #include <vector>
    #include <queue>
    #include <cmath>
    #include <stack>
    #include <cstring>
    usingnamespace std;
    #define INF 0xfffffff
    #define maxn 10025
    #define min(a,b) (a<b?a:b)
    int m, n, Time, cnt, top;
    int dfn[maxn], block[maxn], low[maxn], Father[maxn], Stack[maxn];
    vector<int> G[maxn];
    void init()
    {
        memset(dfn, 0, sizeof(dfn));
        memset(low, 0, sizeof(low));
        memset(block, 0, sizeof(block));
        memset(Father, 0, sizeof(Father));
        top = Time = cnt = 0;
    
        for(int i=0; i<=n; i++)
            G[i].clear();
    }
    void Tarjan(int u,int fa)
    {
        dfn[u] = low[u] = ++Time;
        Father[u] = fa;
        Stack[top++] = u;
        int len = G[u].size(), v, k = 0;
    
        for(int i=0; i<len; i++)
        {
            v = G[u][i];
            if(v == fa && !k)
            {
                k ++;
                continue;
            }
    
            if(!low[v])
            {
                Tarjan(v, u);
                low[u] = min(low[u], low[v]);
            }
            else
                low[u] = min(low[u], dfn[v]);
        }
    
        if(dfn[u] == low[u])
        {
            do
            {
                v = Stack[--top];
                block[v] = cnt;
            }while(u != v);
            cnt ++;
        }
    }
    
    void solve()
    {
        int i, degree[maxn] = {0}, ans = 0;
        for(i=1; i<=n; i++)
        {
            if( !low[i] )
                Tarjan(i, i);
        }
    
        for(i=1; i<=n; i++)
        {
            int v = Father[i];
            if(block[i] != block[v])
            {
                degree[block[i] ] ++;
                degree[block[v] ] ++;
            }
        }
    
        for(i=0; i<cnt; i++)
        {
            if(degree[i] == 1)
                ans ++;
        }
        printf("%d
    ", (ans+1)/2 );
    }
    
    
    int main()
    {
        while(scanf("%d %d",&n, &m) != EOF)
        {
            init();
            while(m --)
            {
                int a, b;
                scanf("%d %d",&a, &b);
                G[a].push_back(b);
                G[b].push_back(a);
            }
            solve();
        }
        return0;
    }
     
     
     
     
     
     
  • 相关阅读:
    JavaScript进阶-BOM和DOM
    JavaScript基础
    CSS2-属性
    CSS1-选择器
    HTML-常用标签
    判断回文
    课堂作业
    动手动脑
    原码反码补码
    Java第一次考试作业
  • 原文地址:https://www.cnblogs.com/chenchengxun/p/4718736.html
Copyright © 2020-2023  润新知