Warm up——缩点树上直径

Posted j666

tags:

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

题目链接

题意:

给出n个点和m条边的无向图,存在重边,问加一条边以后,剩下的桥的数量最少为多少。

题解:

把这个无向图缩点后会得到一个只由桥来连接的图(可以说这个图中的所有边都是桥,相当于一棵树),

然后我们只需要找出来这棵树的最大直径(即相距最远的两个点)。

因为如果我们把直径所在的两个端点连起来,这样减少的桥最多。

所以 答案就是 桥的数量 - 树的直径上桥的数量

原图求桥的数量,缩点后建立新的图求直径

技术图片

 

 

代码:

技术图片
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
#include <queue>
using namespace std;

const int maxn = 200005;//点数
const int maxm = 2001000;//边数,因为是无向图,所以这个值要*2

struct Edge
{
    int to,next;
    bool cut;//是否是桥标记
} edge[maxm],edge1[maxm];
int head[maxn],tot,head1[maxn],tot1;
int low[maxn],dfn[maxn],Stack[maxn],belong[maxn];//belong数组的值是1~scc
int Index,top;
int scc;//边双连通块数/强连通分量的个数
bool Instack[maxn];
int bridge;//桥的数目
bool cut[maxn];
void addedge(int u,int v)
{
    edge[tot].to = v;
    edge[tot].next = head[u];
    edge[tot].cut=false;
    head[u] = tot++;
}
void add(int u,int v)
{
    edge1[tot1].to = v;
    edge1[tot1].next = head1[u];
    head1[u] = tot1++;
}
void Tarjan(int u,int pre)
{
    int v;
    low[u] = dfn[u] = ++Index;
    Stack[top++] = u;
    Instack[u] = true;
    int son=0;
    int flag=0;
    for(int i = head[u]; i != -1; i = edge[i].next)
    {
        v = edge[i].to;
        if(v == pre && !flag)
        {
            flag++;
            continue;
        }
        if( !dfn[v] )
        {
            son++;
            Tarjan(v,u);
            if( low[u] > low[v] )low[u] = low[v];
            if(low[v] > dfn[u])
            {
                bridge++;
                edge[i].cut = true;
                edge[i^1].cut = true;
            }
            if(u == pre && son > 1)cut[u] = true;
            if(u != pre && low[v] >= dfn[u])cut[u] = true;

        }
        else if( Instack[v] && low[u] > dfn[v] )
            low[u] = dfn[v];
    }

    if(low[u] == dfn[u])
    {
        scc++;
        do
        {
            v = Stack[--top];
            Instack[v] = false;
            belong[v] = scc;
        }
        while( v!=u );
    }

}
void init()
{
    tot = 0;
    memset(head,-1,sizeof(head));
}
void init1()
{
    tot1 = 0;
    memset(head1,-1,sizeof(head1));
}
int deep[maxn];

int bfs(int s)
{
    queue<int> Q;
    memset(deep,0,sizeof deep);
    deep[s] = 1;
    Q.push(s);
    int ans = s;
    while(!Q.empty())
    {
        int u = ans = Q.front();
        Q.pop();
        for(int i=head1[u]; ~i; i=edge1[i].next)if(!deep[edge1[i].to])
            {
                int v=edge1[i].to;
                deep[v] = deep[u]+1;
                Q.push(v);
            }
    }
    return ans;

}

void solve(int n)
{
    memset(dfn,0,sizeof(dfn));
    memset(Instack,false,sizeof(Instack));
    memset(cut,0,sizeof cut);
    Index = top = scc = 0;
    bridge = 0;


    for(int i = 1; i <= n; i++)
        if(!dfn[i])
            Tarjan(i,i);

    init1();
    for(int i=1; i<=n; i++)
    {
        for(int j=head[i]; ~j; j=edge[j].next)
        {
            int u=belong[i];
            int v=belong[edge[j].to];
            if(u!=v)
            {
                add(u,v);
                add(v,u);
            }
        }
    }
    int ans=deep[bfs(bfs(1))];

    printf("%d
",scc-1-(ans-1));
}


int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m) && (n || m))
    {
        init();
        while(m--)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            addedge(u,v);
            addedge(v,u);
        }
        solve(n);
    }
    return 0;
}
/*
4 4
1 2
1 3
1 4
2 3
0 0
*/
View Code

 

以上是关于Warm up——缩点树上直径的主要内容,如果未能解决你的问题,请参考以下文章

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

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

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

HDU 4612 Warm up

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

HDU4612 Warm up