【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\n",(ans+1)/2);
27 }return 0;}//Paul_Guderian

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

转载于:https://www.cnblogs.com/Paul-Guderian/p/7018900.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值