Gym 100712H Bridges

    An edge in an undirected graph is a ​bridge​ if after removing it the graph will be disconnected. 
Given an undirected connected graph, you are allowed to add one edge between any pair of nodes so that the 
total number of bridges in the graph is minimized. 
    Input 
    The first line of input contains ​T (1 ≤ T ≤ 64)​ that represents the number of test cases. 
The first line of each test case contains two integers: ​N (3 ≤ N ≤ 100,000) ​and ​M (N-1 ≤ M ≤ 100,000)​, 
where ​N​ is the number of nodes, and ​M​ is the number of edges. 
Each of the following ​M​ lines contains two space-separated integers: ​X Y (1 ≤ X, Y ≤ N)(X ≠ Y)​, which 
means that there is an edge between node ​X​ and node ​Y​. 
It is guaranteed that each pair of nodes is connected by at most one edge. 
Test cases are separated by a blank line. 
   Output 
   For each test case, print a single line with the minimum possible number of bridges after adding one edge. 
 
Sample Input 

7 7 
1 2 
2 3 
3 1 
3 4 
4 5 
4 6 
6 7 
 
3 3 
1 2 
2 3 

3 1 

Sample Output 



 

题目连接:点击打开链接

题意:给n个点,m条边的图,添加一条边,使得图中的桥最少。


理解:整体的思路是Tarjan求出桥的数量,利用并查集在Tarjan过程中进行缩点,缩完点之后重新建一个图,这个图是个树,求出树的直径。桥的数量减去直径就是答案。


红字是这道题涉及的所有知识点,用了一周学了里面不会的东西,写下来自己的理解。


以下都是我自己的理解


:删除图中的一条边,那么这个图就要变成互不联通的2个子图。相关知识点可以看看大神博客(点击打开链接


缩点:将边双联通分量(环)缩成一个点。然后构成的图就是一棵树。


树的直径:任意找一个点跑到离它最远的一个点,再从这个点再跑到它最远点。第二次跑出来的距离就是树的直径。不能理解的话可以画图理解下。记住就行了,想看证明的话可以去搜一下。


CODE:

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string.h>

using namespace std;

const int N = 2000000+10;      ///2倍的边,因为要建2次图

struct node      ///邻接表存图
{
    int st;      ///重建图用
    int en;
    bool bridge; ///标记这条边是否为桥
    int next;
}E[N];

int top;         ///邻接表边数
int n,m;
int bridge;      ///桥的数量
int st1;         ///树的直径第二次dfs起点
int tree_D;      ///树的直径
int dfs_clock;   ///Tarjan时间戳
int fa[N];       ///并查集用
int head[N];     ///邻接表头结点
int dfn[N];      ///定义dfn[u]为u在dfs搜索树(以下简称为树)中被遍历到的次序号
int low[N];      ///定义low[u]为u或u的子树中能通过非父子边追溯到的最早的节点,即dfn[]最小的节点。

void Init()      ///初始化
{
    top = 0;
    bridge = 0;
    tree_D = 0;
    dfs_clock = 0;
    for(int i = 0; i < N; i++)
    {
        head[i] = -1;
        dfn[i] = low[i] = 0;
        fa[i] = i;
    }
}

int Find(int x)
{
    return fa[x] == x?x:fa[x]=Find(fa[x]);
}

void Union(int x,int y)
{
    x = Find(x);
    y = Find(y);
    fa[x] = y;
}

void add(int u,int v)     ///邻接表加边
{
    E[top].st = u;
    E[top].en = v;
    E[top].next = head[u];
    E[top].bridge = false;
    head[u] = top++;
}

void Tarjan(int u,int father)   ///Tarjan缩点
{
    dfn[u] = low[u] = ++dfs_clock;
    for(int i = head[u]; i != -1; i = E[i].next)
    {
        int v = E[i].en;
        if(v == father) continue;
        if(!dfn[v])
        {
            Tarjan(v,u);
            low[u] = min(low[u],low[v]);
            if(dfn[u] < low[v])
            {
                E[i].bridge = true;
                E[i^1].bridge = true;
                bridge++;
            }
            else
            {
                Union(u,v);
            }
        }
        else
            low[u] = min(low[u],dfn[v]);
    }
}

void Find_Tree_D(int u,int father,int dep) ///求树直径,寻找离一个点最远的一个点
{
    if(dep > tree_D)
    {
        tree_D = dep;
        st1 = u;
    }
    for(int i = head[u]; i != -1; i = E[i].next)
    {
        int v = E[i].en;
        if(v == father) continue;
        Find_Tree_D(v,u,dep+1);
    }
}

int main(void)
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        Init();
        scanf("%d%d",&n,&m);
        for(int i = 1; i <= m; i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            add(u,v);
            add(v,u);
        }
        Tarjan(1,-1);
        memset(head,-1,sizeof head);   ///第二次建图之前要初始化头结点
        int st;
        for(int i = 0; i < top; i++)
        {
            if(E[i].bridge)
            {
                int u = Find(E[i].st);
                int v = Find(E[i].en);
                add(u,v);
                st = u;   ///起点必须是图中存在的点,不是联通分量里面的点
            }
        }
        if(bridge)
        {
            Find_Tree_D(st,-1,0);
            Find_Tree_D(st1,-1,0);
        }
        printf("%d\n",bridge-tree_D);

    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值