码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2015-08-10 19:46:55      阅读:140      评论:0      收藏:0      [点我收藏+]

标签:

大致题意:

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

       现在牧场主拥有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\n", (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;
}

 

 
 
 
 
 
 
技术分享
技术分享

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

标签:

原文地址:http://www.cnblogs.com/chenchengxun/p/4718736.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!