【UVA10972】RevolC FaeLoN (求边双联通分量)
题意:
给你一个无向图,要求把所有无向边改成有向边,并且添加最少的有向边,使得新的有向图强联通。
分析:
这题的解法还是很好想的。先用边双联通分量缩点,然后找新图中入度为0和为1的点,入度为0则ans+2,为1则ans+1,最后输出(ans+1)/2。
注意,如果原图本来就强联通,答案为0不是1。
在这里主要说说打边双联通的注意事项。(一开始觉得是跟点双连通差不多的,调试的时候才发现很容易疏忽导致BUG很多啊)
1、如果有重边,则那条就不是割边了,我们很容易向上重走树枝边的反向边导致程序认为这是返祖边。在点双连通中判断一下是不是父亲即可,但边双联通不行。(因为重边对点双连通无影响,但对边双联通有影响)所以要做一个标记,走树枝边的时候把反向边也标记一下。
2、stack中的剩余元素最后要记得pop出来。
3、图不一定联通,要for一遍再dfs。
4、发现(x,y)为割边的时候,(dfn[x]<dfn[y]),边双联通分量是算到y而不是x。(跟点双连通有一点不一样)
代码如下:
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 #include<stack> 7 using namespace std; 8 #define Maxn 1010 9 10 struct node 11 { 12 int x,y,next; 13 bool vis; 14 }t[2*Maxn*Maxn];int len; 15 16 int first[Maxn],n,m; 17 int dfn[Maxn],low[Maxn],cnt; 18 int cc[Maxn],cl,sum[Maxn]; 19 stack<int > s; 20 21 void ins(int x,int y) 22 { 23 t[++len].x=x;t[len].y=y;t[len].vis=0; 24 t[len].next=first[x];first[x]=len; 25 } 26 27 int mymin(int x,int y) {return x<y?x:y;} 28 29 void ffind(int x) 30 { 31 dfn[x]=low[x]=++cnt; 32 s.push(x); 33 for(int i=first[x];i;i=t[i].next) if(!t[i].vis) 34 { 35 int y=t[i].y; 36 t[i].vis=1;t[i+(i%2==1?1:-1)].vis=1; 37 if(!dfn[y]) 38 { 39 ffind(y); 40 low[x]=mymin(low[x],low[y]); 41 if(low[y]>dfn[x]) 42 { 43 cl++; 44 while(!s.empty()) 45 { 46 int z=s.top(); 47 s.pop(); 48 cc[z]=cl; 49 if(z==y) break; 50 } 51 } 52 } 53 else low[x]=mymin(low[x],dfn[y]); 54 } 55 } 56 57 int main() 58 { 59 while(scanf("%d%d",&n,&m)!=EOF) 60 { 61 len=0;cnt=0;cl=0; 62 memset(first,0,sizeof(first)); 63 memset(dfn,0,sizeof(dfn)); 64 memset(cc,0,sizeof(cc)); 65 memset(sum,0,sizeof(sum)); 66 for(int i=1;i<=m;i++) 67 { 68 int x,y; 69 scanf("%d%d",&x,&y); 70 ins(x,y);ins(y,x); 71 } 72 if(!s.empty()) s.pop(); 73 for(int i=1;i<=n;i++)if(!dfn[i]) 74 { 75 ffind(i); 76 if(!s.empty()) 77 { 78 cl++; 79 while(!s.empty()) 80 { 81 cc[s.top()]=cl; 82 s.pop(); 83 } 84 } 85 } 86 if(cl==1) {printf("0\n");continue;} 87 88 int ans=0; 89 for(int i=1;i<=len;i++) 90 { 91 if(cc[t[i].x]==cc[t[i].y]) continue; 92 sum[cc[t[i].x]]++; 93 } 94 for(int i=1;i<=cl;i++) if(sum[i]==1) ans++; 95 else if(sum[i]==0) ans+=2; 96 printf("%d\n",(ans+1)/2); 97 } 98 return 0; 99 }
2016-03-23 13:54:57