HDU 4612 Warm up(手动扩栈,求树上哪两个点的距离最远)

题目大意:

给你一个无向图,问加一条边之后最少还剩下几座桥。

(注意重边处理)

分析:其实当我们把边双连通分量给求出来之后我们就能将连通块求出来,这样我们就可以重新构图。重新构造出来的图肯定是一颗树了,

那么问题就转化为求树的哪两个节点的距离最长。我们可以随便找一个点S开始BFS, BFS到这个点最远的那个点P,然后再从这个最远点P开始BFS,BFS到P最远的点E,  PE之间的距离就是这个图上最大的距离。

注:此题需要手动扩栈

#pragma comment(linker, "/STACK:102400000,102400000")
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <queue>
#include <cmath>
#include <stack>
#include <cstring>
usingnamespace std;
#define INF 0xfffffff
#define maxn 200005
#define min(a,b) (a<b?a:b)
int m, n, Time, top, cnt;
int blocks[maxn], dfn[maxn], low[maxn], Stacks[maxn], Father[maxn];
int step[maxn];
vector<vector<int> > G;
vector<vector<int> > G2;
void init()
{
    memset(dfn, 0, sizeof(dfn));
    memset(low, 0, sizeof(low));
    memset(blocks, 0, sizeof(blocks));
    Time = top = cnt = 0;
    G.clear();
    G.resize(n+2);

    G2.clear();
    G2.resize(n+2);
}
void Tarjan(int u,int fa)
{
    dfn[u] = low[u] = ++Time;
    Stacks[top ++] = u;
    Father[u] = fa;
    int len = G[u].size(), k = 0, v;

    for(int i=0; i<len; i++)
    {
        v = G[u][i];

        if( !k && fa == v)
        {
            k ++;
            continue;
        }

        if( !low[v] )
        {
            Tarjan(v, u);
            low[u] = min(low[u], low[v]);
        }
        else
            low[u] = min(low[u], dfn[v]);
    }

    if(dfn[u] == low[u])
    {
        do
        {
            v = Stacks[--top];
            blocks[v] = cnt;
        }while(u != v);
        cnt ++;
    }
}
int BFS(int s,int flag)
{
    int P, Pn;
    queue<int> Q;
    Q.push(s);
    memset(step, -1, sizeof(step));
    step[s] = 0;
    while( Q.size() )
    {
        P = Q.front();
        Q.pop();

        int len = G2[P].size();

        for(int i=0; i<len; i++)
        {
            Pn = G2[P][i];

            if( step[Pn] == -1)
            {
                step[Pn] = step[P] + 1;
                Q.push(Pn);
            }
        }
    }

    if(flag == 1)
        return P;
    return step[P];
}
void solve()
{
    int ans = 0, i;
    for(i=1; i<=n; i++)
    {
        if(!low[i])
            Tarjan(i, i);
    }

    for(i=1; i<=n; i++)
    {
        int v = Father[i];

        if(blocks[i] != blocks[v])
        {   /**重新构图*/
            G2[blocks[i] ].push_back(blocks[v]);
            G2[blocks[v] ].push_back(blocks[i]);
        }
    }

    int p = BFS(0,1);///以0为起点经行一次BFS返回最远距离的编号

    ans = cnt - BFS(p, 2) - 1;///返回最远距离的长度
    printf("%d\n", ans);

}

int main()
{
    while(scanf("%d %d",&n, &m), m+n)
    {
        init();
        while(m--)
        {
            int a, b;
            scanf("%d %d",&a, &b);
            G[a].push_back(b);
            G[b].push_back(a);
        }
        solve();
    }
    return0;
}
/*
5 4
1 2
1 3
1 4
2 5
*/
时间: 2024-10-09 12:43:47

HDU 4612 Warm up(手动扩栈,求树上哪两个点的距离最远)的相关文章

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 (双连通分支+数的直径)

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

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

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

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

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 bet

输入输出挂,手动扩栈。

//#pragma comment(linker, "/STACK:102400000,102400000") c++扩栈 /* int __size__ = 256 << 20;//256 兆 char * __p__ = (char *)malloc(__size__) + __size__; __asm__("movl %0,%%esp\n"::"r"(__p__)); */ /* inline bool scanf_(int

HDU 4612 Warm up

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

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

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

【HDU 4612 Warm up】BCC 树的直径

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