zoukankan      html  css  js  c++  java
  • 【RevolC FaeLoN Uva 10972】

    ·无向图转有向图,经典而美妙

    ·英文题,述大意:

          输入一个无向图(不一定联通),现在询问:是否可以将每一条无向边定向,并为新图添加最少的新的有向边,使得原图强联通。

    ·分析:

          静静地分析有向图强连通分量与无向连通图的关系。如果能够发现这两者之间的转化关系,那么就是这道题的解决方案(因为这就是本题题意)。

    ·解决问题前半部分的一个关键思路:

          什么样的无向连通图才能满足:必定存在一种将所有无向边定向的方案,使得新图是一个有向图强联通分量?我们可以美妙地知道,对于一个无向图强联通分量,任意(u,v),都可以u到v,v到u——这是一个环的体现。

    所以,得出结论:是双连通分量的无向图可以转化为有向图强连通分量。

    image

    ·回到问题。那么我们可以找出原图的双连通分量,对于每个双连通分量中的点,它们在这个图变成有向图后,依然能够互相通达(即满足强连通性,比如上图这样的定向方式)。

    ·所以现在要解决的是:处在不同双连通分量中的点之间该怎么办。

    ·引入“缩点”思想,即将找出的双连通分量简化为一个点(因为双连通分量内的边是不会贡献答案,影响决策的)。

    image

    ·我们可以将紫色的边称为桥(Bridge)。

    ·现在的问题是:我们怎样将这个树的边定向,以满足题目要求?我们知道,光靠这些树上的边是不足以构成无向图强连通分量的。所以要添加尽量少(题目要求)有向边,使得条件满足。

    ·我们还差这样一步:如何快速求出我们到底需要添加多少边。

    根据之前的结论,如果我们能够将每个点置身于至少一个环中,那么任意点之间就可以相互通达。所以整个问题现在就纠结在了一处:给你一棵树,你需要高效地求出,至少再连几条新的有向边,使得这个树的每个点至少在一个环中(注意,现在的点是小点点们缩成的大点)。

    ·随意地观赏一棵树:
    image

    ·像图中那样连边,第一次连边,红色的点已经处在环中了,还剩两个点,那么就再连接一次,就可以使得所有的点处在环中。所以我们work out的结论是:将两个叶子结点之间连一条边,并且尽量是的这两个叶子的最近公共祖先是根节点(如果不让根节点处在某一环中,请画图去发现问题)。下面为了方便理解,在画出几种情况:

    image

    ·在图中,大米饼偷偷的给边加了方向。一切明了。我们可以发现,将一棵树转化成强联通分量,其实就是这样的美妙,可概括成这样一副简图:

    image

    ·最终结果浮出水面:需要添加的边数=(叶子数+1)/2

    ·完结。整理思路地说,我们首先要求出输入的无向图中所有的双连通分量,然后缩点形成一棵树,计算树的叶子节点数,然后根据上文结论得答案。

    ·代码堵在二环路了。它发了条短信过来:

    ①本题的图可能不连通,那么会使得缩点后出现森林!如图:

    image

    ·我们进行耐心处理:

    (1)对于节点数大于1的树怎样连:【收尾相接,连入整体】

    合并后计算公式就不变,所以总计算式还是统计叶子数就可以了。

    image

    (2)对于只有一个点的树怎样连向整体:【至少两条,连入整体】

    因此我们需要单独处理这样的点(注意,这个点是大点!)直接将ANS加上2就可以了。

    image

    ·总结:最终答案为:

    ANS=(单点的个数*2+度为1的节点)/2[度为1就是叶子结点]

    ②简化代码:本题的双连通指的是边双连通(想一想,肯定呀),所以计算双连通分量的方法是找到所有的桥,然后dfs一遍不经过桥,每一坨就是一个双连通分量。但是,我们发现,在找桥的Tarjan()中,每个点的low[]就已经可以表示一种属性——不同的双连通分量了。所以可以直接省去第二次搜索,按照low[]来缩点。后来你可以预见到,缩点只是一种思想,在实际操作中我们不需要去建立新边,你只需要枚举这个大点(即一个双连通分量)中的每个小点所连的边延伸到了几个其他的强连通分量(只要low不同,就不是相同双连通分量),最后你有发现练连了几个联通分量就是这个大点的度。一切都变得美好了。

    ·代码到来.

     1 #include<stdio.h>
     2 #include<algorithm>
     3 #include<cstring>
     4 #define go(i,a,b) for(int i=a;i<=b;i++)
     5 #define fo(i,a,x) for(int i=a[x],v=e[i].v;i>-1;i=e[i].next,v=e[i].v)
     6 #define mem(a,b) memset(a,b,sizeof(a))
     7 using namespace std;const int N=5003;struct E{int v,next;}e[N*100];
     8 int n,m,head[N],k,low[N],dfn[N],dfs_clock,num,ans,degree[N];bool Name[N];
     9 void ADD(int u,int v){e[k]=(E){v,head[u]};head[u]=k++;}
    10 void Sign(int u,int fa)
    11 {
    12     low[u]=dfn[u]=++dfs_clock;fo(i,head,u)if(v!=fa){if(!dfn[v]){
    13         Sign(v,u);low[u]=min(low[u],low[v]);if(dfn[u]<low[v])num++;
    14     }else low[u]=min(low[u],dfn[v]);}
    15 }
    16 int main(){while(~scanf("%d%d",&n,&m))
    17 {
    18     mem(head,-1);mem(dfn,0);mem(low,0);mem(Name,0);
    19     mem(degree,0);dfs_clock=num=ans=k=0;
    20     go(i,1,m){int u,v;scanf("%d%d",&u,&v);ADD(u,v);ADD(v,u);}
    21     go(u,1,n)if(!dfn[u])num++,Sign(u,-1);if(num==1){puts("0");continue;}
    22     
    23     go(u,1,n){Name[low[u]]=1;fo(i,head,u)Name[low[v]]=1,
    24         degree[low[u]]+=(low[u]!=low[v]);}
    25     go(u,1,n)ans+=(!degree[u]&&Name[u])*2,ans+=degree[u]==1;
    26     printf("%d
    ",(ans+1)/2);
    27 }return 0;}//Paul_Guderian

    我看到了你,看到来时的路,看到那些尘封的痛哭与晃动.——汪峰《谢谢》

  • 相关阅读:
    AJAX
    1.InfluxDB-官方测试数据导入
    storm报错:Exception in thread "main" java.lang.RuntimeException: InvalidTopologyException(msg:Component: [mybolt] subscribes from non-existent stream: [default] of component [kafka_spout])
    J07-Java IO流总结七 《 InputStreamReader和OutputStreamWriter 》
    J06-Java IO流总结六 《 BufferedReader和BufferedWriter 》
    J05-Java IO流总结五 《 BufferedInputStream和BufferedOutputStream 》
    J04-Java IO流总结四 《 FileReader和FileWriter 》
    J03-Java IO流总结三 《 FileInputStream和FileOutputStream 》
    J02-Java IO流总结二 《概述》
    J01-Java IO流总结一 《异常捕获》
  • 原文地址:https://www.cnblogs.com/Paul-Guderian/p/7018900.html
Copyright © 2011-2022 走看看