算法学习——求割点与桥的tarjan算法 HDU4738

前天打周赛做到 HDU4738  绞尽脑汁都没想到用什么好的方法来解决这个问题   周赛结束之后跟Yasola和xcy讨论了一下居然用到  tarjan算法   exm???  tarjan不是用来求 lca的么???

回去怒补了一发才知道   tarjan原来是一系列是算法   根据我看到的博客原文  可以这么说   tarjan是个天才  他伟大的一生创造了无数的算法  统称 tarjan算法  其中有三个最有名的  求lca  求图的割点与桥 (还有一个啥来着    反正我最近一定要学一下   无奈看完之后就断电熄灯了   一直拖到昨晚才学


基本概念:

图论里的桥  即 连接两个子图唯一边   如果这条边被你砍了  整个图将被你一分为二

图论里的割点  同桥的理解   只是对象是一个结点  如果这个结点被你分开   那么整个图也将分裂


因为我也是刚学   hdu4738 的对象也是一张无向图  以下均以无向图进行讨论   有向图日后再说


在tarjan算法中  对于一张图  我以深搜为核心  并且每个节点只访问一次  那么最后深搜得到的图   会是什么??

是一棵树!!  (这真是太奇妙了    我自认为对搜索有点信心   但是从来没有想过这样的应用

于是有如下定理:

在无向连通图G中

1.根结点为割点  当且仅当它有两个及以上的子结点

2.非根节点为割点   当且仅当 u 存在结点 v 及其所有后代都没有反向边可以返回 u 的祖先 (不包括 u


我们在深搜过程中每一个节点都有他的访问次序  我们定义一个  时间戳 dfn 来表示

另外我们还需要一个  定义一个  low 表示 该结点反向能达到的最小的  dfn   ( 初始都是 low=dfn

虽然我们深搜出一棵树   但是我们的研究对象还是原图   对于原图来说   每一个结点都会有若干条边 

如果在这时  我的结点 u 有一条到 v 的边  而且  low[v] <= dfn[u] 这就表示在 u 结点有一条边  可以返回到之前的结点 并形成一个环  那么显而易见 这根本不会是桥

相反  只要对每一个边访问   若  low[v] > dfn [u] 那么这条边即为 桥


大犇都是先理解割点再理解桥的   像我这种菜鸡只能从  割点的特殊情况桥 开始理解

对于割点的话道理 还是差不多   无非是一个 等号的问题

在判断桥中   如果 low[v]==dfn[u] 那么这意味着始末都是这个 u 结点的一个 环而已  (包括自环) 没什么影响  只要是环  那么必然不是桥

而对于割点   如果 low[v]==dfn[u] 那么v及其所有子结点都必须通过 u 结点来访问 u 的祖先  所以 u 仍然是割点


附带 HDU 4738

E - Caocao's Bridges
Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u

Description

Caocao was defeated by Zhuge Liang and Zhou Yu in the battle of Chibi. But he wouldn't give up. Caocao's army still was not good at water battles, so he came up with another idea. He built many islands in the Changjiang river, and based on those islands, Caocao's army could easily attack Zhou Yu's troop. Caocao also built bridges connecting islands. If all islands were connected by bridges, Caocao's army could be deployed very conveniently among those islands. Zhou Yu couldn't stand with that, so he wanted to destroy some Caocao's bridges so one or more islands would be seperated from other islands. But Zhou Yu had only one bomb which was left by Zhuge Liang, so he could only destroy one bridge. Zhou Yu must send someone carrying the bomb to destroy the bridge. There might be guards on bridges. The soldier number of the bombing team couldn't be less than the guard number of a bridge, or the mission would fail. Please figure out as least how many soldiers Zhou Yu have to sent to complete the island seperating mission.

Input

There are no more than 12 test cases. 

In each test case: 

The first line contains two integers, N and M, meaning that there are N islands and M bridges. All the islands are numbered from 1 to N. ( 2 <= N <= 1000, 0 < M <= N 2 ) 

Next M lines describes M bridges. Each line contains three integers U,V and W, meaning that there is a bridge connecting island U and island V, and there are W guards on that bridge. ( U ≠ V and 0 <= W <= 10,000 ) 

The input ends with N = 0 and M = 0.

Output

For each test case, print the minimum soldier number Zhou Yu had to send to complete the mission. If Zhou Yu couldn't succeed any way, print -1 instead.

Sample Input

3 3
1 2 7
2 3 4
3 1 4
3 2
1 2 7
2 3 4
0 0

Sample Output

-1
4

这题就是很裸的求桥的问题   只不过 求最短的桥 并且有两个坑点 

我们在用tarjan 的时候吧每个桥都保存在一个数组中  最后for 一遍即可

而坑点包括 

1.原图可能不联通   这时不需要派人去炸桥 直接输出 0

2.有重边

3.可能有权值为0的桥  但我们必须要有一个人去带炸弹啊 所以这是输出 1

ac code

/* ***********************************************************************
   > File Name: contest.cpp
   > Author: Key
   > Mail: keyld777@gmail.com
   > Created Time: 2016年10月30日 星期日 20时10分38秒
 ********************************************************************** */
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>

using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 1010;
int n, m, indx, vis_time, union_num, ansn;
int head[maxn], low[maxn], dfn[maxn], ans[maxn];

struct node {
    int to;
    int next;
    int w;
};
node edge[2 * maxn * maxn];

void AddEdge(int u, int v, int w)
{
    edge[indx].to = v;
    edge[indx].w = w;
    edge[indx].next = head[u];
    head[u] = indx++;

    edge[indx].to = u;
    edge[indx].w = w;
    edge[indx].next = head[v];
    head[v] = indx++;
}

void init()
{
    vis_time = indx = ansn = 0;
    union_num = 1;
    memset(head, -1, sizeof head);
    memset(dfn, 0, sizeof dfn);
    memset(low, 0, sizeof low);
}

void tarjan(int u, int father)
{
    low[u] = dfn[u] = ++vis_time;
    int pre_num = 0;
    for (int id = head[u]; id != -1; id = edge[id].next) {
        int v = edge[id].to;
        if (v == father && pre_num == 0) {
            pre_num++;
            continue;
        }
        if (!dfn[v]) {
            union_num++;
            tarjan(v, u);
            low[u] = min(low[u], low[v]);
            if (low[v] > dfn[u])
                ans[ansn++] = edge[id].w;
        } else if (dfn[v] < dfn[u])
            low[u] = min(low[u], dfn[v]);
    }
}

int main()
{
    int u, v, w;
    while (scanf("%d %d", &n, &m) != EOF) {
        if (n == 0 && m == 0)
            break;
        init();
        for (int i = 0; i < m; i++) {
            scanf("%d %d %d", &u, &v, &w);
            AddEdge(u, v, w);
        }
        tarjan(1, 1);
        if (union_num < n)
            puts("0");
        else {
            int true_ans = inf;
            for (int i = 0; i < ansn; i++)
                if (true_ans > ans[i])
                    true_ans = ans[i];
            if (true_ans == 0)
                true_ans++;
            else if (true_ans == inf)
                true_ans = -1;
            printf("%d\n", true_ans);
        }
    }
    return 0;
}



  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值