zoukankan      html  css  js  c++  java
  • Hdu4005-The war(双连通缩点)

    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
    

    题意: 敌人在N个点间建了M条线路,每条线路都有一个权值,敌人要再建一条线路,己方可以毁掉敌方的一条线路,问己方最少要花多少钱(至少得准备多少钱)才能使

    这些点不连通。

    解析: 先分离出每个双连通分量(删除双联通分量中的任何一条边还是连通的,没有意义),给每个分量重新编一个号,缩成一个点,在Tarjan算法过程中把桥保存下来。

    选一条权值最小的桥,分别从两头出发,找次小的桥。答案就是次小的桥的权值,为甚么是找次小的桥,因为如果敌人是把最小的桥所连的两个连通分量再加一条边,则

    必须删次小的桥,如果是其他,则只需要删最小的桥即可。

    代码

    #include<cstdio>
    #include<cstring>
    #include<string>
    #include<iostream>
    #include<sstream>
    #include<algorithm>
    #include<utility>
    #include<vector>
    #include<set>
    #include<map>
    #include<queue>
    #include<cmath>
    #include<iterator>
    #include<stack>
    using namespace std;
    typedef __int64 LL;
    const int INF=1e9+7;
    const double eps=1e-7;
    const int maxn=10005;
    const int maxm=100005;
    int N,M,eid,id,top;
    int scc_id,scc[maxn],dfn[maxn],low[maxn],b[maxn],fa[maxn];
    int head[maxn],KK[maxn];
    struct edge
    {
        int v,w,next;
        edge(int v=0,int w=0,int next=-1):v(v),w(w),next(next){}
    }E[2*maxm];
    struct node
    {
        int u,v,w;
        node(int u=0,int v=0,int w=0):u(u),v(v),w(w){}
    };
    vector<node> V;
    void init()
    {
        eid=id=top=scc_id=0;
        for(int i=0;i<=N;i++)
        {
            head[i]=-1;
            dfn[i]=low[i]=b[i]=scc[i]=0;
            fa[i]=i;
        }
        V.clear();
    }
    void AddEdge(int u,int v,int w)
    {
        E[++eid]=edge(v,w,head[u]);
        head[u]=eid;
    }
    void Tarjan(int u)
    {
        dfn[u]=low[u]=++id;
        KK[top++]=u;
        bool first=false;
        for(int i=head[u];i!=-1;i=E[i].next)
        {
            edge& e=E[i];
            int v=e.v,w=e.w;
            if(v==fa[u]&&!first){ first=true; continue; }
            if(!dfn[v])
            {
                fa[v]=u;
                Tarjan(v);
                low[u]=min(low[u],low[v]);
                if(low[v]>dfn[u]) V.push_back(node(u,v,w));//这条边是桥
            }
            else low[u]=min(low[u],dfn[v]);
        }
        int t;
        if(dfn[u]==low[u])
        {
            scc_id++;  //双连通分量
            do
            {
                t=KK[--top];
                b[t]=scc_id; //连通分量编号
            }while(u!=t);
        }
        return;
    }
    int ans;
    int FindPath(int u,int pre)
    {
        int Min=INF,MMin=INF;
        for(int i=head[u];i!=-1;i=E[i].next)
        {
            int v=E[i].v,w=E[i].w;
            if(v==pre) continue;
            int t=FindPath(v,u);
            if(t<MMin) MMin=t;
            if(w<MMin) MMin=w;
            if(Min>MMin) swap(Min,MMin);
        }
        ans=min(ans,MMin);
        return Min;
    }
    int main()
    {
        while(scanf("%d%d",&N,&M)!=EOF)
        {
            init();
            int u,v,w;
            for(int i=1;i<=M;i++)
            {
                scanf("%d%d%d",&u,&v,&w);
                AddEdge(u,v,w); //建边
                AddEdge(v,u,w); //反向边
            }
            for(int i=1;i<=N;i++) if(!dfn[i]) Tarjan(i); //找连通分量
    
            int mindist=INF,picku,pickv,Size=V.size();
            eid=0;
            memset(head,-1,sizeof(head));
            for(int i=0;i<Size;i++) //重新建图
            {
                node& t=V[i];
                int u=t.u,v=t.v,w=t.w;
                AddEdge(b[u],b[v],w);
                AddEdge(b[v],b[u],w);
                if(w<mindist){ mindist=w,picku=b[u],pickv=b[v]; } //权值最小的桥
            }
            ans=INF;
            FindPath(picku,pickv);
            FindPath(pickv,picku);
            if(ans==INF) printf("-1
    ");
            else printf("%d
    ",ans);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    洛谷【P1480】A/B Problem
    bzoj 2654 && bzoj 3675 总结
    关于三维莫队问题的一些思考和探究
    BZOJ 1179 抢掠计划atm (缩点+有向无环图DP)
    BZOJ 1500 Luogu P2042 [NOI2005] 维护数列 (Splay)
    Codeforces 919D Substring (拓扑图DP)
    【学习笔记】有向无环图上的DP
    【学习笔记】求解简单递归式的一般方法
    BZOJ 3930 Luogu P3172 选数 (莫比乌斯反演)
    POJ 1061 BZOJ 1477 Luogu P1516 青蛙的约会 (扩展欧几里得算法)
  • 原文地址:https://www.cnblogs.com/wust-ouyangli/p/5723009.html
Copyright © 2011-2022 走看看