HDU 4612——Warm up——————【边双连通分量、树的直径】

                              Warm up

Time Limit:5000MS     Memory Limit:65535KB     64bit IO Format:%I64d & %I64u

Submit Status Practice HDU 4612

Description

  N planets are connected by M bidirectional channels that allow instant transportation. It‘s always possible to travel between any two planets through these channels. 
  If we can isolate some planets from others by breaking only one channel , the channel is called a bridge of the transportation system.
People don‘t like to be isolated. So they ask what‘s the minimal number of bridges they can have if they decide to build a new channel. 
  Note that there could be more than one channel between two planets.

Input

  The input contains multiple cases. 
  Each case starts with two positive integers N and M , indicating the number of planets and the number of channels. 
  (2<=N<=200000, 1<=M<=1000000) 
  Next M lines each contains two positive integers A and B, indicating a channel between planet A and B in the system. Planets are numbered by 1..N. 
  A line with two integers ‘0‘ terminates the input.

Output

  For each case, output the minimal number of bridges after building a new channel in a line.

Sample Input

4 4

1 2

1 3

1 4

2 3

0 0

Sample Output

0

题目大意:有n颗行星,有m条双向通道连接着m对行星。问你新建一条双向通道后,无向图中最少会剩下多少条桥。有重边。

解题思路:无向图求边双连通分量,缩点,重新构图,形成树。求树的直径,然后用原图总的桥减去树的直径即为结果。求树的直径,我们用两次搜索,第一次从任意点出发,搜到的最远结点即为直径的一端,然后从这一端再次进行搜索,搜到直径的另一端。

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
#include<stack>
using namespace std;
const int maxn = 200100;
struct Edge{
    int from,to,dist,next;
    Edge(){}
    Edge(int _to,int _next):to(_to),next(_next){}
}edges[maxn*10];
int head[maxn], tot;
int dfs_clock, dfn[maxn], brinum;
int Stack[maxn], instack[maxn], top, ebccno[maxn], ebcc_cnt;
int deg[maxn];
vector<int>G[maxn];
void init(){
    tot = 0;
    brinum = dfs_clock = 0;
    top = 0;
    ebcc_cnt = 0;
    memset(deg,0,sizeof(deg));
    memset(head,-1,sizeof(head));
}
void AddEdge(int _u,int _v){
    edges[tot] = Edge(_v,head[_u]);
    head[_u] = tot++;
}
int dfs(int u,int fa){
    int lowu = dfn[u] = ++dfs_clock;
    Stack[++top] = u;
   // instack[u] = 1;
    for(int i = head[u]; i != -1; i = edges[i].next){
        int v = edges[i].to;
        if(!dfn[v]){
            int lowv = dfs(v,i);
            lowu = min(lowu,lowv);
            if(lowv > dfn[u]){
                brinum++;
            }
        }else if(dfn[v] < dfn[u] && (fa^1) != i){//这里用边的编号来标记是否是同一条边的回边
            lowu = min(lowu,dfn[v]);
        }
    }
    if(dfn[u] == lowu){     //找到一个边双连通分量
        ebcc_cnt++;
        for(;;){
            int v = Stack[top--];
         //   instack[v] = 0;
            ebccno[v] = ebcc_cnt;   //给每个点划分一个分量标号
            if(u == v){
                break;
            }
        }
    }
  //  low[u] = lowu;
    return lowu;
}
void find_ebcc(int n){
    memset(dfn,0,sizeof(dfn));
    memset(instack,0,sizeof(instack));
    for(int i = 1; i <= n; i++){
        if(!dfn[i]){
            dfs(i,-1);
        }
    }
}
int pos, Maxd;
void dfs1(int u,int dep,int fa){    //求树的直径
    if(dep > Maxd){
        Maxd = dep;
        pos = u;
    }
    for(int i = 0; i < G[u].size(); i++){
        int v = G[u][i];
        if(fa == v){ continue; }
        dfs1(v,dep+1,u);
    }
}
int main(){
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF&&(n+m)){
        init();
        for(int i = 0; i <= n; i++){
            G[i].clear();
        }
        int a,b;
        for(int i = 0; i < m; i++){
            scanf("%d%d",&a,&b);
            AddEdge(a,b);
            AddEdge(b,a);
        }
        find_ebcc(n);
        for(int i = 1; i <= n; i++){
            for(int j = head[i]; j != -1; j = edges[j].next){
                int v = edges[j].to;
                if(ebccno[i] != ebccno[v]){     //重新构图,形成树
                    G[ebccno[i]].push_back(ebccno[v]);
                }
            }
        }
        pos = 1, Maxd = 0;
        dfs1(1,0,-1);
        int st = pos; Maxd = 0;
        dfs1(pos,0,-1);
        printf("%d\n",brinum - Maxd);
    }
    return 0;
}

  

时间: 2024-07-30 10:17:41

HDU 4612——Warm up——————【边双连通分量、树的直径】的相关文章

HDU 4612 Warm up(双连通分量缩点+求树的直径)

思路:强连通分量缩点,建立一颗新的树,然后求树的最长直径,然后加上一条边能够去掉的桥数,就是直径的长度. 树的直径长度的求法:两次bfs可以求,第一次随便找一个点u,然后进行bfs搜到的最后一个点v,一定是直径的一个端点(证明从略),第二次以点v为开头进行bfs,求出的最后一个点,就是直径的另一个端点,记录深度就是我们要求的长度.我这里是使用的bfs+dfs,是一样的,少开一个deep数组,节省一下空间吧…… 其实我一开始是不会求的,我以为随便一个叶子节点就可以做端点,交上去WA,当时还好奇感觉

hdoj 4612 Warm up【双连通分量求桥&amp;&amp;缩点建新图求树的直径】

Warm up Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)Total Submission(s): 5093    Accepted Submission(s): 1131 Problem Description N planets are connected by M bidirectional channels that allow instant transport

hdu 4612 Warm up 双连通缩点+树的直径

首先双连通缩点建立新图(顺带求原图的总的桥数,其实由于原图是一个强连通图,所以桥就等于缩点后的边) 此时得到的图类似树结构,对于新图求一次直径,也就是最长链. 我们新建的边就一定是连接这条最长链的首尾,这样就将原图的桥减少了直径个. #include<iostream> #include<cstring> #include<cstdio> #include<vector> #include<algorithm> #include<map&g

Hdu 4612 Warm up (双连通分支+数的直径)

题目链接: Hdu 4612 Warm up 题目描述: 给一个无向连通图,问加上一条边后,桥的数目最少会有几个? 解题思路: 题目描述很清楚,题目也很裸,就是一眼看穿怎么做的,先求出来双连通分量,然后缩点重新建图,用bfs求树的直径,直径的长度就是减去桥的数目. 这个题目需要手动扩展,而且手动扩展的话要用C++提交,G++re哭了. 1 #include <cstdio> 2 #include <queue> 3 #include <cstring> 4 #inclu

HDU 4612 Warm up(边双联通求树的直径)

Problem Description N planets are connected by M bidirectional channels that allow instant transportation. It's always possible to travel between any two planets through these channels. If we can isolate some planets from others by breaking only one

[HDOJ4612]Warm up(双连通分量,缩点,树直径)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4612 所有图论题都要往树上考虑 题意:给一张图,仅允许添加一条边,问能干掉的最多条桥有多少. 必须解决重边的问题,最后会说. 首先tarjan跑出所有的双连通分量和是桥的边还有桥的数量,这非常重要.接着缩点重新建图,然后两遍dfs找出两个在树上距离最远的点.我的想法就是把这条最长的链连成一个环,让它成为一个双连通分量,这样的效果是最好的.最后就是用桥的数量减去树直径再减一就得到了剩下的桥的数量了.求

【HDU 4612 Warm up】BCC 树的直径

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=4612 题意:一个包含n个节点m条边的无向连通图(无自环,可能有重边).求添加一条边后最少剩余的桥的数目. 思路:要想尽可能地消灭桥,那么添加的这条边一定是连通了最多的BCC. 所以首先进行双连通分量分解,并记录桥的数目:然后将同属一个BCC的点缩成一个,代之以block序号,以block序号为点将原图重构为一棵树. 最后求树的直径,桥的数目减去树的直径即为答案. 整体框架是学习了 http://w

HDU 4612 Warm up —— (缩点 + 求树的直径)

题意:一个无向图,问建立一条新边以后桥的最小数量. 分析:缩点以后,找出新图的树的直径,将这两点连接即可. 但是题目有个note:两点之间可能有重边!而用普通的vector保存边的话,用v!=fa的话是没办法让重边访问的,因此,使用数组模拟邻接表的方法来储存边. 这样,只要访问了一条边以后,令E[i].vis=E[i^1].vis=1即可,这样可以防止无向图的边和重边搞混.原理就是按位异或,一个奇数^1变为比它小1的偶数,反之亦然:如5^1=4,4^1=5. 具体见代码: 1 #include

HDU 4612 Warm up

给出一个无向图,你可以加一条边,使得加上边之后的图割边数量最少. 方法:每个边双连通分量缩点,形成一个树,树上的每条边都是割边,割边数量为S1: 接下来只要算出树上最长路径(树的直径)就可以了,最长路径两段连线,路径上的割边都不可能再成为割边了,记最长路径长度为S2: Ans=S1-S2: 第一步可以用Tarjan算法 树的直径可以这样求:先随便找一个点P,DFS一下,找到与P距离最远的那个点St:然后从St出发,再进行一次DFS,与St相距最远的那个点的距离就是树的直径. 这个题目搞了一天,一