zoukankan      html  css  js  c++  java
  • POJ 3177 Redundant Paths (边双连通+缩点)

    <题目链接>

    <转载于 >>>  >

    题目大意:

    有n个牧场,Bessie 要从一个牧场到另一个牧场,要求至少要有2条独立的路可以走。现已有m条路,求至少要新建多少条路,使得任何两个牧场之间至少有两条独立的路。两条独立的路是指:没有公共边的路,但可以经过同一个中间顶点。

    解题分析:

    在同一个边双连通分量中,任意两点都有至少两条独立路可达,所以同一个边双连通分量里的所有点可以看做同一个点。

    缩点后,新图是一棵树,树的边就是原无向图的桥。

    现在问题转化为:在树中至少添加多少条边能使图变为双连通图。

    结论:添加边数=(树中度为1的节点数+1)/2

    具体方法为,首先把两个最近公共祖先最远的两个叶节点之间连接一条边,这样可以把这两个点到祖先的路径上所有点收缩到一起,因为一个形成的环一定是双连通的。然后再找两个最近公共祖先最远的两个叶节点,这样一对一对找完,恰好是(leaf+1)/2次,把所有点收缩到了一起。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    const int N = 5e3+5 , M = 1e4+5;
    #define clr(a,b) memset(a,b,sizeof(a))
    struct Edge{
        int to,next;
    }edge[M<<1];
    
    int head[N],low[N],dfn[N],belong[N],deg[N],stk[N],instk[N];
    int n,m,tot,cnt,top,scc;
    void addEdge(int u,int v){
        edge[tot].to=v,edge[tot].next=head[u];
        head[u]=tot++;
    }
    void init(){
        tot=cnt=top=scc=0;
        clr(head,-1);clr(low,0);clr(dfn,0);clr(instk,0);clr(deg,0);
    }
    void Tarjan(int u,int fa){
        low[u]=dfn[u]=++cnt;
        stk[++top]=u;instk[u]=1;
        for(int i=head[u];~i;i=edge[i].next){
            int v=edge[i].to;
            if(i==(fa^1))continue;   //不能用搜索树上的边来更新low值,这种写法能够用来处理重边的情况
            if(!dfn[v]){
                Tarjan(v,i);
                low[u]=min(low[u],low[v]);
            }
            else if(instk[v])      //此时栈里的所有元素均属于同一边双连通分量,找连通分量的根的时候一定要规定这点,否则可能会与其他连通分量的dfn比较
                low[u]=min(low[u],dfn[v]);     //low值全部等于该双连通分量中最先遍历的点dfn值
        }
        if(dfn[u]==low[u]){
            ++scc;   
            while(true){
                int v=stk[top--];
                instk[v]=0;
                belong[v]=scc;   //将该联通块中的所有点全部缩点染色
                if(v==u)break;
            }     
        }
    }
    int main(){
        while(scanf("%d%d",&n,&m)!=EOF){
            init();
            for(int i=1;i<=m;i++){
                int u,v;scanf("%d%d",&u,&v);
                addEdge(u,v),addEdge(v,u);
            }
            Tarjan(1,-1);
            for(int i=1;i<=n;i++){
                for(int j=head[i];j!=-1;j=edge[j].next){
                    int v=edge[j].to;
                    if(belong[i]!=belong[v])
                        deg[belong[i]]++;      //求出缩点后每个点的度
                }
            }
            int sum=0;
            for(int i=1;i<=scc;i++)if(deg[i]==1)sum++;  //寻找度为1的叶子节点  
            int ans=(sum+1)/2;      
            printf("%d
    ",ans);
        }
    }

    2018-11-07

  • 相关阅读:
    OnGUI 音频
    Java 8 的一些新特性
    获取文件编码格式
    js 常用 正则
    C#中这个算是什么
    数据的批量增加
    Ehcache的配置(自学,有问题请指出)
    Linux 下配置和使用java、Tomcat
    StringBuffer和StringBuildr的区别
    Oracle中复制一张表的结构,用sql语句复制一张表结构
  • 原文地址:https://www.cnblogs.com/00isok/p/9919653.html
Copyright © 2011-2022 走看看