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

Posted icode-xiaohu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDU 4612 Warm up(双连通分量缩点+求树的直径)相关的知识,希望对你有一定的参考价值。

  思路:强连通分量缩点,建立一颗新的树,然后求树的最长直径,然后加上一条边能够去掉的桥数,就是直径的长度。

  树的直径长度的求法:两次bfs可以求,第一次随便找一个点u,然后进行bfs搜到的最后一个点v,一定是直径的一个端点(证明从略),第二次以点v为开头进行bfs,求出的最后一个点,就是直径的另一个端点,记录深度就是我们要求的长度。我这里是使用的bfs+dfs,是一样的,少开一个deep数组,节省一下空间吧……

  其实我一开始是不会求的,我以为随便一个叶子节点就可以做端点,交上去WA,当时还好奇感觉没错,结果随便一画,就错了……

  注意:这个题里面有重边,我们可以通过重边走回父亲节点,但不可以通过父亲边回到父亲节点,所以原先标记父亲的方式是不可以的,这里改成标记边,在Edge结构体中多加一个vis的变量,标记这个边是否被访问过,那这样重边就能正常访问了。

  这个题的数据量比较强,容易爆栈,STL的实用性就不强了,所以尽量使用数组模拟,数组能够开到更大的空间。

  感想:感觉这一块的题目真心很爱出错,这个题真是WA了好多次才过的,感觉oj不爱我了……

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
#define N 200020
#define M 2000020
struct Edge
{
    int to,nxt,vis;
} edge[M];
int head[N],dfn[N],low[N],vis[N],sta[N],id[N];
int tot,all,top,scc,bridge;
void addedge(int a,int b,int flag)
{
    edge[tot].to = b;
    edge[tot].nxt = head[a];
    edge[tot].vis = flag;
    head[a] = tot++;
}
void tarjan(int u)
{
    vis[u] = 1;
    dfn[u] = low[u] = ++all;
    sta[top++] = u;
    for(int i = head[u]; i != -1; i = edge[i].nxt)
    {
        if(edge[i].vis) continue;
        int v = edge[i].to;
        edge[i].vis = edge[i^1].vis = 1;
        if(!dfn[v])
        {
            tarjan(v);
            low[u] = min(low[u],low[v]);
            if(low[v] > dfn[u]) bridge++;
        }
        else if(vis[v]) low[u] = min(low[u],dfn[v]);
    }
    if(low[u] == dfn[u])
    {
        scc++;
        int num;
        do
        {
            num = sta[--top];
            id[num] = scc;
            vis[num] = 0;
        }
        while(u != num);
    }
}
vector<int> tree[N];
int treevis[N],maxdeep;
void dfs(int u,int deep)
{
    treevis[u] = 1;
    maxdeep = max(deep,maxdeep);
    int len = tree[u].size();
    for(int i = 0; i < len; i++)
    {
        int v = tree[u][i];
        if(!treevis[v])
        {
            dfs(v,deep+1);
        }
    }
}
void init()
{
    memset(vis,0,sizeof(vis));
    memset(dfn,0,sizeof(vis));
    memset(id,0,sizeof(id));
    memset(low,0,sizeof(low));
    all = 0;
    top = 0;
    scc = 0;
    bridge = 0;
}
int que[N];
int getstart()
{
    int front,rear;
    front = rear = 0;
    que[rear++] = 1;
    treevis[1] = 1;
    while(front != rear)
    {
        int now = que[front++];
        int len = tree[now].size();
        for(int i = 0; i < len; i++)
        {
            int nxt = tree[now][i];
            if(treevis[nxt]) continue;
            treevis[nxt] = 1;
            que[rear++] = nxt;
        }
    }
    return que[--rear];
}
int main()
{
    int n,m,a,b;
    while(~scanf("%d%d",&n,&m))
    {
        if(!n && !m) break;
        memset(head,-1,sizeof(head));
        tot = 0;
        for(int i = 0; i < m; i++)
        {
            scanf("%d%d",&a,&b);
            addedge(a,b,0);
            addedge(b,a,0);
        }
        init();
        tarjan(1);
        for(int i = 1; i <= scc; i++)
        {
            tree[i].clear();
        }
        for(int u = 1; u <= n; u++)
        {
            for(int i = head[u]; i != -1; i = edge[i].nxt)
            {
                int v = edge[i].to;
                if(id[u] != id[v])
                {
                    int uu = id[u];
                    int vv = id[v];
                    tree[uu].push_back(vv);
                    tree[vv].push_back(uu);
                }
            }
        }
        maxdeep = 0;
        memset(treevis,0,sizeof(treevis));
        int start = getstart();
        memset(treevis,0,sizeof(treevis));
        dfs(start,1);
        printf("%d\n",scc-maxdeep);
    }
    return 0;
}

 

以上是关于HDU 4612 Warm up(双连通分量缩点+求树的直径)的主要内容,如果未能解决你的问题,请参考以下文章

hdu4612-Warm up(边的双连通分量)

Warm up HDU - 4612( 树的直径 边双连通分量)

HDU4612 Warm up

HDOJ4612双连通分量缩点+找树的直径

HDU4612:Warm up(缩点+树的直径)

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