POJ - 3177 Redundant Paths (边双连通 tarjan)

POJ - 3177

题意+思路:问最少能补成几条边,使这个图成为边双连通。

具体的做法是把每个双连通分量缩成一个点,(求边双连通分量: 先找桥,然后把所有的桥从图中删去,这样图中剩下的几块连起来的子图,每个子图是一个边双连通分量),缩点的时候,把所有的桥加回来,而这题所求的加最少的边来补全成双连通图,ans = (num(leaf) + 1) / 2 ;

原因:缩点后的图,可以看作一棵树,树上的每条边都是需要补充的桥,那么我们肯定是先找树上路径最长的,再找次长的,直到成为一个双连通。树上路径最长的起点和终点肯定是在叶子结点上,所以每次找lca最远的两个叶子结点,把他们连起来,所有偶数个叶子节点的时候,刚好是num(leaf)/2,奇数就是(num(leaf)+1)/2,所以就有了那个式子。

这题的去重我是直接在add()加边的时候操作的,但是这样其实不够快,毕竟每次都要找以u为起点的边。

参考了一下别人的代码,tarjan中会有一个参数fa来判断是否是走过来的那条边,如果用这条边在edge中的下标来传递参数,那么在tarjan中就可以判断一下到底这条边是走过来的那条边,还是重复的边了,如果是重复的边,还是让它去往下去更新,这样就能得到正确的结果。


#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>

using namespace std;
const int maxe = 1e4 + 10, maxn = 5e3 + 50;
struct node
{
    int to,next;
    int tag,isb;    //判断重边与是否为桥
    node(){}
    node(int a,int b,int c,int d) {to = a; next = b, tag = c, isb = d;}
}edge[maxe << 2];
int h[maxn], dfn[maxn], low[maxn], bid[maxn] , vis[maxn];
int in[maxn], isb[maxn];
int n,m;
int edgenum,tot;
void init()
{
    for(int i = 1; i <= n; i++)
        h[i] = -1, low[i] = dfn[i] = bid[i] = vis[i] = 0;
    for(int i = 1; i <= n ; i++)
        bid[i] = in[i] = isb[i] = 0;
    edgenum = tot = 0;
}
void add(int u,int to)
{
    int flag = -1;       //判重
    for(int i = h[u]; ~i; i = edge[i].next)
    {
        int v = edge[i].to;
        if(v == to) {flag = i;break;}
    }
    if(~flag) {edge[flag].tag = 1; return;}
    edge[edgenum] = node(to,h[u],0,0);
    h[u] = edgenum++;
}
int tarjan(int u,int pre)       //找桥标记一下
{
    dfn[u] = low[u] = ++tot;
    for(int i = h[u]; ~i ; i = edge[i].next)
    {
        int v = edge[i].to;
        if(!dfn[v])
        {
            tarjan(v,u);
            low[u] = min(low[u],low[v]);
            if(!edge[i].tag && low[v] > dfn[u])
            {
                edge[i].isb = 1;
                edge[i^1].isb = 1;
            }
        }
        else if(dfn[v] < dfn[u] && v != pre)
            low[u] = min(low[u],dfn[v]);
    }
    return 0;
}
void shrink(int u,int root,int pre)
{
    bid[u] = root;
    vis[u] = true;
    for(int i = h[u]; ~i; i = edge[i].next)
    {
        int v = edge[i].to;
        if(!edge[i].isb && v != pre && !vis[v])
            shrink(v,root,u);
    }
}
int solve()
{
    for(int i = 1; i <= n ; i++)
    for(int j = h[i]; ~j ; j = edge[j].next)
    {
        int u = i, v = edge[j].to;
        if(edge[j].isb == 0) continue;  //如果不是桥
        in[bid[u]] ++;
    }
    int ans = 0;

    //for(int i = 1;i <= n ; i++)
    //    cout << isb[i] << " " << in[i] << endl;
    for(int i = 1; i <= n ; i++)
        if(isb[i] && in[i] == 1) ans++;
    printf("%d\n",(ans+1)/2);
    return 0;
}
int main()
{
    //freopen("D://in.txt","r",stdin);
    while(~scanf("%d%d",&n,&m))
    {
        init();
        for(int i = 0; i < m ; i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            add(a,b);   add(b,a);
        }
        //找桥标记桥
        for(int i = 1 ; i <= n ; i++)
            if(!dfn[i]) tarjan(i,-1);

        /*for(int i = 1; i <= n; i++)
            for(int j = h[i]; ~j; j = edge[j].next)
        {
            int v = edge[j].to;
            cout <<"f to isb "<< i << " "  << v << " " << edge[j].isb << endl;
        }
        */
        for(int i = 1 ; i <= n ; i++)
            if(!bid[i]) { isb[i] = true; shrink(i,i,-1); }
        solve();
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值