双连通分量_road

问一个无向图上面,连最少的边,使之成为一个双连通图.求出连边的具体方法。

一开始首先要找出图里的双连通分量, 把他们缩成一个点后再处理.

【双连通分量】

求双连通分量的方法和Tarjan差不多, 改一下就好了.

定义: "割点" & "桥" (上网有很多啊, 传送门:http://www.byvoid.com/blog/biconnect/zh-hans/

题目中要求的是“边双联通”,所以求的是桥.

定义: DFN & LOW (DFN是在DFS树里序号, LOW是不经过父亲边可到达的最浅深度)

所以, LOW[X]= MIN (MIN   (DFN[Y])  {X---Y为横叉边,到达的是自己的祖先}   , MIN(LOW[Y]) {X---Y是到达未访问过的子节点}   )

桥,即为 对于一条边(U----V) ,  DFN[U] < LOW[V]  (儿子节点不能到达比自己浅的节点)

遇到桥时, 就把儿子所在的子树全部取出,标记.

Pro: 

void tarjan(int x,int last){
                dfn[x]=low[x]=++indexs;
                s[++top]=x; // 进栈 
                for(int p=end[x]; p>0; p=next[p]){
                        if((p^1) ==last) continue; // 若不等于上一次的边 
                        else {
                             if(dfn[to[p]]==0) {        // 若没有访问 
                                tarjan(to[p],p);
                                if (low[to[p]]< low[x]) low[x]=low[to[p]]; // 更改 low 
                                else if ( low[to[p]]> dfn[x] ){  // p 边为 "桥" 
                                    while(s[top]!=to[p]) col[s[top--]]=num;  // 依次退栈 
                                    col[to[p]]=num;
                                    --top;
                                    num++;
                                    }
                                }
                             else oMin(low[x], dfn[to[p]]);
                             }
                        }
     }

还好啦.

【求最少边数】

传送门见。

【求边】

每一个点,对于他的每一个子节点, 把他与自己合并。

若某一时刻点的节点数大于2(等于2 就没有边往上连),就把它们某两个节点(不在一个子树)相连, 再合并成一个点。

对于根节点, 若只有一个子树,把节点里的每一个点与根连;

否则 若有两个点, 他们相连; 一个点, 与根相连。

Pro:

state Dfs(int x,int last){
              
             state a;
             a.t=0;
              
             bool leaf=1;
             for(int p=end[x]; p>0; p=next[p]){
                     if(to[p] != last ){
                          leaf=0;
                          state c=Dfs(to[p], x);
                          if(a.t+ c.t>2) {   // 相连 ---
                                  line(c.a, a.a); 
                                  if(a.t+c.t==4){a.t=2; a.a= a.b; a.b=c.b;}   // -4个点-
                                  else if(a.t==2){a.t=1; a.a=a.b;}           //  -3个点-
                                  else {a.t=1; a.a=c.b;}
                                  }
                          else {
                                if(a.t==1) {a.t++; a.b=c.a;}                //  直接合并
                                else {a=c;}
                                }
                          }
                     }
             if(leaf){ ++ans; state ret; ret.t=1; ret.a=x; return ret; }
             return a;
     }
      
void work(){
            ans=Edge=0;
            int root= low[1];
            state q=Dfs(low[1],0);
            int time=0;
            for(int p=end[root]; p>0; p=next[p]) ++time;    // 特判
            if(time==0) {printf("0\n"); return;}
            if(time==1) {
                        line(q.a, root);
                        if(q.t==2) line(q.b, root);
                        }
            else {
                 if(q.t==1) line(q.a, root);
                 else line(q.a, q.b);
                 }
            printf("%d\n",(ans+1)/2);
            for(int i=1; i<=(ans+1)/2; i++) printf("%d %d\n",l[i][0], l[i][1]);
     }




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值