连通专题 点连通分量+树的直径

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4612
题目大意:n个点由m条无向边连接,求加一条边后桥的最少数量,有重边,并且重边不算桥。

思路:tarjan算法求出所有的桥,得到一颗生成树,我们再加一条边必然成环,要使得桥的数量最少,就得使得这个环中的边最多。于是找这棵树距离根最远的两条链(深度最大)。连接起来。答案就是n-1-D1-D2

好像这个思路没有问题。实际上是有问题的。例如:

3, 6, 9深度都一样。但答案显然是9和3或者9和6不是3 6。因为两条最长的链可能共用节点。所以正确的思路应该是n-1-直径。

而且这个模板对重边适用。

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
const int maxm=1e6+10;
struct Node
{
    int to;
    int next; //起点相同的上一条边下标
    bool cut; //边是否为桥
}edge[maxm*2];//双向边开两倍空间

int cut;      //边数
int ans=0;
int head[maxn];//点i的最后一条边
int dfn[maxn];
int low[maxn];
int tot;
int bvis[maxn];//点属于哪个边连通分量
int N;         //连通分量数

void addEdge(int u, int v)
{
    edge[cut].to=v;
    edge[cut].next=head[u];
    edge[cut].cut=0;
    head[u]=cut++;
}

void Tarjan(int u, int e)//求出所有的桥
{
    low[u]=dfn[u]=++tot;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].to;
        if(i==(e^1))//这里只会去重该边的反边,不会去它的重边(所以模板对重边适用,不被计算成桥)
        {
            continue;
        }
        if(dfn[v]==0)
        {
            Tarjan(v, i);
            low[u]=min(low[u], low[v]);

            if(low[v]>dfn[u])//桥
            {
                ans++;
                edge[i].cut=1;
                edge[i^1].cut=1;
            }
        }
        else if(dfn[v]<dfn[u])
        {
            low[u]=min(low[u], dfn[v]);
        }
    }
}

void dfs(int u)//求出每个点所在的边连通分量
{
    dfn[u]=1;
    bvis[u]=N;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        if(edge[i].cut)
        {
            continue;
        }
        if(dfn[edge[i].to]==0)
        {
            dfs(edge[i].to);
        }
    }
}

vector<int> e[maxn];
int d, k;
int vis[maxn];
void DFS(int u, int s)
{
    vis[u]=1;
    if(s>d)
    {
        k=u, d=s;
    }
    for(int i=0;i<e[u].size();i++)
    {
        int v=e[u][i];
        if(!vis[v])
        {
            DFS(v, s+1);
        }
    }
}
int main()
{
    int n,m,a,b;
    while(scanf("%d%d",&n,&m),m*n)
    {
        ans=0;
        for(int i=0;i<maxn;i++)
        {
            e[i].clear();
        }
        memset(head, -1, sizeof(head));

        cut=0;
        for(int i=0;i<m;i++)
        {
            scanf("%d%d",&a,&b);
            addEdge(a, b);
            addEdge(b, a);
        }

        memset(dfn, 0, sizeof(dfn));
        memset(bvis, 0, sizeof(bvis));
        tot=N=0;
        for(int i=1;i<=n;i++)
        {
            if(dfn[i]==0)
            {
                Tarjan(i, -1);
            }
        }

        memset(dfn, 0, sizeof(dfn));

        for(int i=1;i<=n;i++)
        {
            if(dfn[i]==0)
            {
                N++;
                dfs(i);
            }
        }

        for(int u=1;u<=n;u++)
        {
            for(int i=head[u];i!=-1;i=edge[i].next)
            {
                int v=edge[i].to;
                if(bvis[u]!=bvis[v])
                {
                    e[bvis[u]].push_back(bvis[v]);
                    e[bvis[v]].push_back(bvis[u]);
                }
            }
        }
        memset(vis, 0, sizeof(vis));
        d=k=0;
        DFS(1, 0);
        memset(vis, 0, sizeof(vis));
        DFS(k ,0);
        cout<<ans-d<<endl;
    }

    return 0;
}
全部评论

相关推荐

10-17 16:04
已编辑
南京大学 C++
陈启鸣:恭喜
投递腾讯等公司10个岗位
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务