zoukankan      html  css  js  c++  java
  • Redundant Paths 分离的路径(边双连通分量)

    题干:
      为了从F(1≤F≤5000)个草场中的一个走到另一个,贝茜和她的同伴们有时不得不路过一些她们讨厌的可怕的树.奶牛们已经厌倦了被迫走某一条路,所以她们想建一些新路,使每一对草场之间都会至少有两条相互分离的路径,这样她们就有多一些选择.每对草场之间已经有至少一条路径.给出所有 R ( F - 1 ≤ R ≤ 10000 )条双向路的描述,每条路连接了两个不同的草场,请计算最少的新建道路的数量, 路径由若干道路首尾相连而成.两条路径相互分离,是指两条路径没有一条重合的道路.但是,两条分离的路径上可以有一些相同的草场. 对于同一对草场之间,可能已经有两条不同的道路,你也可以在它们之间再建一条道路,作为另一条不同的道路,请输出最少的需要新建的道路数.

    题解:

      加减道路?加减边?比较简单地就可以想到边双连通分量

      本题要求使每一个节点都可由至少两条道路到达,那么满足条件的节点就是度数 >= 2 的节点(无向图的度)。度数为 1 的节点就是不满足题意的。

      在两点间建边有三种情况:
      1、两个度数 >= 2 的节点相连:没用,舍掉。

      2、一个度数 >= 2 ,另一个 = 1 的两个节点相连:使一个节点满足题意。

      3、两个度数 = 1 的节点相连:使两个节点满足题意。

      十分显然,两个度数 = 1 的节点相连一定是最优的。这也表明,本题核心在于找到 度数 = 1 的节点。

    100%  错解(点双连通分量):

      像以往的点双连通分量,先缩点,求一下度数即可。

      但这样的 AC 代码并不符合点双连通分量的求法(在实现过程中,需要在 tarjan 中判断掉父节点;在建边中,需要判掉重边)。

      只能说在这道题中,数据并没有考虑这种打法,虽然 AC,一定是错解。

    Code:

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 #define $ 5111
     5 using namespace std;
     6 int m,n,k,t,dfn[$],low[$],first[$],tot1,tar,sta[$],up,circle,tr=-1;
     7 int sum,cir[$],out[$];
     8 bool judge[$],vis[$][$];
     9 struct tree{    int to,next;    }a[$<<5],aa[$<<5];
    10 inline int min(int x,int y){    return x<y?x:y;    }
    11 inline void add(int x,int y){
    12     a[++tot1]=(tree){    y,first[x]    };
    13     first[x]=tot1;
    14     a[++tot1]=(tree){    x,first[y]    };
    15     first[y]=tot1;
    16 }
    17 inline void tarjan(int x,int dad,int tmp=0){
    18     dfn[x]=low[x]=++tar;
    19     sta[++up]=x;
    20     for(register int i=first[x];i;i=a[i].next){
    21         int to=a[i].to;
    22         if(to==dad) continue;
    23         if(!dfn[to]){
    24             tarjan(to,x);
    25             low[x]=min(low[x],low[to]);
    26         }
    27         else low[x]=min(low[x],dfn[to]);
    28     }
    29     if(dfn[x]==low[x]){
    30         ++circle;
    31         do{
    32             tmp=sta[up--];
    33             cir[tmp]=circle;
    34         }while(tmp!=x);
    35     }
    36 }
    37 signed main(){
    38     scanf("%d%d",&n,&m);
    39     for(register int i=1,x,y;i<=m;++i){
    40         scanf("%d%d",&x,&y);
    41         if(vis[x][y]==0) add(x,y),vis[x][y]=vis[y][x]=1;
    42     }
    43     tarjan(1,0);
    44     for(register int i=1;i<=n;++i)
    45         for(register int j=first[i];j;j=a[j].next){
    46             int to=a[j].to;
    47             if(cir[i]!=cir[to]) out[cir[i]]++;
    48         }
    49     for(register int i=1;i<=circle;++i) if(out[i]<2) sum++;
    50     circle==1?puts("0"):printf("%d
    ",(sum+1)/2);
    51 }
    View Code

    100%  正解(边双连通分量):

      正解当然是边双连通分量。

     1 inline void tarjan(int x,int opt){
     2     dfn[x]=low[x]=++tar;
     3     for(register int i=first[x];i;i=a[i].next){
     4         int to=a[i].to;
     5         if(!dfn[to]){
     6             tarjan(to,i);
     7             low[x]=min(low[x],low[to]);
     8             if(low[to]>dfn[x]) br[i]=br[i^1]=1; 
     9         }
    10         else if(i!=(opt^1)) low[x]=min(low[x],dfn[to]);
    11     }
    12 }
    13 inline void dfs(int x){
    14     in[x]=dcc;
    15     for(register int i=first[x];i;i=a[i].next){
    16         int to=a[i].to;
    17         if(in[to]||br[i]) continue;
    18         dfs(to);
    19     }
    20 }

      同样是先缩点,求一下度数即可。

    Code:

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 #define $ 5111
     5 using namespace std;
     6 int m,n,sum,dfn[$],low[$],first[$],tot,tar,br[$],up,circle,dcc,in[$],out[$];
     7 struct tree{    int to,next;    }a[$<<5],aa[$<<5];
     8 inline int min(int x,int y){    return x<y?x:y;    }
     9 inline void add(int x,int y){
    10     a[++tot]=(tree){    y,first[x]    };
    11     first[x]=tot;
    12     a[++tot]=(tree){    x,first[y]    };
    13     first[y]=tot;
    14 }
    15 inline void tarjan(int x,int opt){
    16     dfn[x]=low[x]=++tar;
    17     for(register int i=first[x];i;i=a[i].next){
    18         int to=a[i].to;
    19         if(!dfn[to]){
    20             tarjan(to,i);
    21             low[x]=min(low[x],low[to]);
    22             if(low[to]>dfn[x]) br[i]=br[i^1]=1; 
    23         }
    24         else if(i!=(opt^1)) low[x]=min(low[x],dfn[to]);
    25     }
    26 }
    27 inline void dfs(int x){
    28     in[x]=dcc;
    29     for(register int i=first[x];i;i=a[i].next){
    30         int to=a[i].to;
    31         if(in[to]||br[i]) continue;
    32         dfs(to);
    33     }
    34 }
    35 signed main(){
    36     scanf("%d%d",&n,&m); tot++;
    37     for(register int i=1,x,y;i<=m;++i) scanf("%d%d",&x,&y),add(x,y);
    38     tarjan(1,0);
    39     for(register int i=1;i<=n;++i)  if(!in[i]) dcc++,dfs(i);
    40     for(register int i=2,x,y;i<=tot;++i){
    41         x=a[i^1].to,y=a[i].to;
    42         if(in[x]!=in[y]) out[in[y]]++;
    43     }
    44     for(register int i=1;i<=dcc;++i) if(out[i]==1) sum++;
    45     dcc==1?puts("0"):printf("%d
    ",(sum+1)/2);
    46 }
    View Code
    越努力 越幸运
  • 相关阅读:
    03:矩形分割 (二分)
    09:膨胀的木棍 (二分+角度计算)
    A Cubic number and A Cubic Number (二分) HDU
    B
    08:不重复地输出数 (set)
    10:河中跳房子 (二分)
    07:和为给定数 (二分)
    04:网线主管 (二分)
    河中跳房子
    010:输出前k大的数
  • 原文地址:https://www.cnblogs.com/OI-zzyy/p/11183705.html
Copyright © 2011-2022 走看看