avatar
fireworks99
keep hungry keep foolish

HDU 4738 Caocao's Bridge(双连通+桥)

无向图中,如果去掉一条边,使得图被分成了几个部分,那么这条边就被称为桥

judge

不在环里的边都是桥

边(from, to)不在环里的标志:low[to] > time[from]

HDU 4738 曹操的桥

无向图求桥

坑点:

  1. 有重边
  2. 初始不连通
  3. 无人守至少1人去

关于vector邻接表、二维邻接矩阵、链式前向星

  1. 普通vector邻接表:无需遍历+没有边权

  2. 二维邻接矩阵:遍历找边+可存边权

  3. 链式前向星:无需遍历+可存边权

  4. 普通vector邻接表+二维邻接矩阵:(利用前者)无需遍历+(利用后者)可存边权

  5. 以结构体为储存类型的vector:无需遍历+可存边权

    3、5较优,1有缺陷,2耗时间,4耗空间

Code of this problem(My Code)

关于处理重边:我用嵌套的map记录重边,重边中的任意一条边均非桥!最后处理时直接跳过

#include <map>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

int n, m, area, bridge;
int cnt, tot, low[1005], time[1005], head[1005];
map<int, map<int, int> > mp;///重边中的任意一条均不是桥
struct edge
{
    bool flag;
    int from, to, w, pre;
} a[2000005];

void add(int from, int to, int w)
{
    a[cnt].flag = 0;
    a[cnt].from = from;
    a[cnt].to = to;
    a[cnt].w = w;
    a[cnt].pre = head[from];
    head[from] = cnt;
    cnt++;
}

void Trajan(int x, int pre)
{
    low[x] = time[x] = ++tot;
    for(int i = head[x]; ~i; i = a[i].pre)
    {
        int to = a[i].to;
//        if(i == (pre ^ 1))///处理重边的方法
//            continue;
        if(to == pre)
            continue;
        if(!time[to])
        {
            Trajan(to, x);
            low[x] = min(low[x], low[to]);
            if(low[to] > time[x])
            {
                bridge++;
                a[i].flag = a[i ^ 1].flag = 1;
            }
        }
        else
            low[x] = min(low[x], time[to]);
    }
}

int main()
{
    while(~scanf("%d%d", &n, &m))
    {
        if(n == 0 && m == 0)
            break;
        int u, v, w;
        mp.clear();
        cnt = tot = area = bridge = 0;
        memset(low, 0, sizeof(low));
        memset(time, 0, sizeof(time));
        memset(head, -1, sizeof(head));
        for(int i = 0; i < m; ++i)
        {
            scanf("%d%d%d", &u, &v, &w);
            add(u, v, w);
            add(v, u, w);
            mp[u][v]++,  mp[v][u]++;
        }
        for(int i = 1; i <= n; ++i)///初始可能不连通的做法
            if(!time[i])
                area++, Trajan(i, -1);///计算连通块
        if(area > 1)
        {
            cout << '0' << '\n';
            continue;
        }
        if(bridge == 0)
        {
            cout << "-1" << '\n';
            continue;
        }
        int ans = 0x3f3f3f3f;
        for(int i = 0; i < 2 * m; ++i)
        {
            u = a[i].from;
            v = a[i].to;
            if(mp[u][v] > 1)
                continue;
            if(a[i].flag && a[i].w < ans)
                ans = a[i].w;
        }
        if(ans == 0x3f3f3f3f)
        {
            cout << "-1" << '\n';
            continue;
        }
        printf("%d\n", ans == 0 ? 1 : ans);
    }
    return 0;
}

另一种处理重边的方法(很简洁但我不懂)2020.4.8懂了

if(i == (pre ^ 1)) continue;

Tarjan函数第二个参数,在上面我自己写的代码中,是x的父节点,在下面标准代码中,是连接x父节点与x(指向x)的那条边,pre ^ 1是pre的反向边,我们要略过反向边,但不能略过其他的重边(同样连接了x与其父节点)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

int n, m, area, bridge;
int cnt, tot, low[1005], time[1005], head[1005];
struct edge
{
    bool flag;
    int to, w, pre;
} a[2000005];

void add(int from, int to, int w)
{
    a[cnt].flag = 0;
    a[cnt].to = to;
    a[cnt].w = w;
    a[cnt].pre = head[from];
    head[from] = cnt;
    cnt++;
}

void Trajan(int x, int pre)
{
    low[x] = time[x] = ++tot;
    for(int i = head[x]; ~i; i = a[i].pre)
    {
        int to = a[i].to;
        if(i == (pre ^ 1))
            continue;
        if(!time[to])
        {
            Trajan(to, i);///注意此处的i
            low[x] = min(low[x], low[to]);
            if(low[to] > time[x])
            {
                bridge++;
                a[i].flag = a[i ^ 1].flag = 1;
            }
        }
        else
            low[x] = min(low[x], time[to]);
    }
}

int main()
{
    while(~scanf("%d%d", &n, &m))
    {
        if(n == 0 && m == 0)
            break;
        int u, v, w;
        cnt = tot = area = bridge = 0;
        memset(low, 0, sizeof(low));
        memset(time, 0, sizeof(time));
        memset(head, -1, sizeof(head));
        for(int i = 0; i < m; ++i)
        {
            scanf("%d%d%d", &u, &v, &w);
            add(u, v, w);
            add(v, u, w);
        }
        for(int i = 1; i <= n; ++i)
            if(!time[i])
                area++, Trajan(i, -1);
        if(area > 1)
        {
            cout << '0' << '\n';
            continue;
        }
        if(bridge == 0)
        {
            cout << "-1" << '\n';
            continue;
        }
        int ans = 0x3f3f3f3f;
        for(int i = 0; i < 2 * m; ++i)
            if(a[i].flag && a[i].w < ans)
                ans = a[i].w;
        printf("%d\n", ans == 0 ? 1 : ans);
    }
    return 0;
}

第二份代码里的形参,第二个已不是第一个的父节点,而是父节点的head值,一条边的标号

Site by Baole Zhao | Powered by Hexo | theme PreciousJoy