hdoj 4005 The war 【边双连通缩点 + 树形dp】



The war

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65768/65768 K (Java/Others)
Total Submission(s): 2365    Accepted Submission(s): 538


Problem Description
In the war, the intelligence about the enemy is very important. Now, our troop has mastered the situation of the enemy's war zones, and known that these war zones can communicate to each other directly or indirectly through the network. We also know the enemy is going to build a new communication line to strengthen their communication network. Our task is to destroy their communication network, so that some of their war zones can't communicate. Each line has its "cost of destroy". If we want to destroy a line, we must spend the "cost of destroy" of this line. We want to finish this task using the least cost, but our enemy is very clever. Now, we know the network they have already built, but we know nothing about the new line which our enemy is going to build. In this condition, your task is to find the minimum cost that no matter where our enemy builds the new line, you can destroy it using the fixed money. Please give the minimum cost. For efficiency, we can only destroy one communication line.
 

Input
The input contains several cases. For each cases, the first line contains two positive integers n, m (1<=n<=10000, 0<=m<=100000) standing for the number of the enemy's war zones (numbered from 1 to n), and the number of lines that our enemy has already build. Then m lines follow. For each line there are three positive integer a, b, c (1<=a, b<=n, 1<=c<=100000), meaning between war zone A and war zone B there is a communication line with the "cost of destroy " c.
 

Output
For each case, if the task can be finished output the minimum cost, or output ‐1.
 

Sample Input
       
       
       
       
3 2 1 2 1 2 3 2 4 3 1 2 1 1 3 2 1 4 3
 

Sample Output
       
       
       
       
-1 3
Hint
For the second sample input: our enemy may build line 2 to 3, 2 to 4, 3 to 4. If they build line 2 to 3, we will destroy line 1 to 4, cost 3. If they build line 2 to 4, we will destroy line 1 to 3, cost 2. If they build line 3 to 4, we will destroy line 1 to 2, cost 1. So, if we want to make sure that we can destroy successfully, the minimum cost is 3.
 

Source
The 36th ACM/ICPC Asia Regional Dalian Site —— Online Contest
 



题意:敌军有n个据点和连接据点的m条无向边,题目保证n个据点可以直接或间接联系,已知敌军可以在图中任意两据点(前提没有边直接相连)建一条边。现在给出破坏每条边的花费,为了让敌军n个据点之间不能相互联系,要求你只破坏一条边达到目的,若可以输出达到目的的最小花费,反之输出-1。

最小花费——意味着不管敌军在哪里建新边,我们达到目的所付出的代价都不能超过它。


分析:首先我们知道——破坏边双连通分量(边数大于1)里面的任意一条边都不能达到目的。这样的话,可以先求出图中所有的边双连通分量,缩点后得到一棵树。原图变成一棵树后,为了找到最小花费,我们必须考虑敌军建边最坏的情况。最坏的情况就是敌军把树中权值最小的边加固了,也就是说我们不能破坏这条边而达到目的。


考虑加固最小权值边<s, e>而建的新边<u, v>,u和v必是 s和e的子节点(或者孙子节点)。这样我们只需每次考虑最坏的情况,每次选择当前节点下最小权值边,这一点可以利用树形dp自底向上实现。


思路:原图求出边双连通分量,缩点后找到最小权值边<s, e>,然后在树上从s和e各DFS一次,找出路径所有节点下边权次小的值,所有次小值中的最小值就是答案。




AC代码:


#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
#define MAXN 10000+100
#define MAXM 200000+10
#define INF 0x3f3f3f3f
using namespace std;
int n, m;
struct Edge{
    int from, to, val, cut, next;
};
Edge edge[MAXM];
int head[MAXN], edgenum;
void init(){
    edgenum = 0;
    memset(head, -1, sizeof(head));
}
void addEdge(int u, int v, int w)
{
    Edge E1 = {u, v, w, 0, head[u]};
    edge[edgenum] = E1;
    head[u] = edgenum++;
    Edge E2 = {v, u, w, 0, head[v]};
    edge[edgenum] = E2;
    head[v] = edgenum++;
}
int low[MAXN], dfn[MAXN];
int dfs_clock;
int ebc_cnt, ebcno[MAXN];
bool Instack[MAXN];
stack<int> S;
int bridge;
void tarjan(int u, int fa)
{
    int v;
    low[u] = dfn[u] = ++dfs_clock;
    S.push(u); Instack[u] = true;
    int have = 1;
    for(int i = head[u]; i != -1; i = edge[i].next)
    {
        v = edge[i].to;
        if(have && v == fa)
        {
            have = 0;
            continue;
        }
        if(!dfn[v])
        {
            tarjan(v, u);
            low[u] = min(low[u], low[v]);
            if(low[v] > dfn[u])
                edge[i].cut = edge[i^1].cut = 1, bridge++;
        }
        else if(Instack[v])
            low[u] = min(low[u], dfn[v]);
    }
    if(low[u] == dfn[u])
    {
        ebc_cnt++;
        for(;;)
        {
            v = S.top(); S.pop();
            Instack[v] = false;
            ebcno[v] = ebc_cnt;
            if(u == v) break;
        }
    }
}
void find_cut(int l, int r)
{
    memset(low, 0, sizeof(low));
    memset(dfn, 0, sizeof(dfn));
    memset(Instack, false, sizeof(Instack));
    memset(ebcno, 0, sizeof(ebcno));
    dfs_clock = ebc_cnt = bridge = 0;
    for(int i = l; i <= r; i++)
        if(!dfn[i]) tarjan(i, -1);
}
void getMap()
{
    init();
    int a, b, c;
    for(int i = 0; i < m; i++)
    {
        scanf("%d%d%d", &a, &b, &c);
        addEdge(a, b, c);
    }
}
struct NEdge{
    int from, to, val, next;
};
NEdge Nedge[MAXM];
int Nhead[MAXN], Nedgenum;
void init_N(){
    Nedgenum = 0;
    memset(Nhead, -1, sizeof(Nhead));
}
void addNEdge(int u, int v, int w)
{
    NEdge E1 = {u, v, w, Nhead[u]};
    Nedge[Nedgenum] = E1;
    Nhead[u] = Nedgenum++;
    NEdge E2 = {v, u, w, Nhead[v]};
    Nedge[Nedgenum] = E2;
    Nhead[v] = Nedgenum++;
}
int rec, s, e;
void suodian()
{
    init_N(); rec = INF;
    for(int i = 0; i < edgenum; i+=2)
    {
        int u = ebcno[edge[i].from];
        int v = ebcno[edge[i].to];
        if(u != v)
        {
            addNEdge(u, v, edge[i].val);
            if(edge[i].val < rec)
            {
                rec = edge[i].val;
                s = u; e = v;//最小边
            }
        }
    }
}
int ans;
int dp[MAXN];
void DFS(int u, int fa)
{
    for(int i = Nhead[u]; i != -1; i = Nedge[i].next)
    {
        int v = Nedge[i].to;
        if(v == fa) continue;
        DFS(v, u);
        dp[v] = min(dp[v], Nedge[i].val);
        if(dp[u] > dp[v])
        {
            ans = min(ans, dp[u]);
            dp[u] = dp[v];
        }
        else
            ans = min(ans, dp[v]);
    }
}
void solve()
{
    find_cut(1, n);
    suodian(); ans = INF;
    memset(dp, INF, sizeof(dp));
    DFS(s, e); DFS(e, s);
    if(ans != INF)
        printf("%d\n", ans);
    else
        printf("-1\n");
}
int main()
{
    while(scanf("%d%d", &n, &m) != EOF)
    {
        getMap();
        solve();
    }
    return 0;
}


你可能感兴趣的:(hdoj 4005 The war 【边双连通缩点 + 树形dp】)