zoukankan      html  css  js  c++  java
  • 【bzoj2521】[Shoi2010]最小生成树 网络流最小割

    题目描述

    Secsa最近对最小生成树问题特别感兴趣。他已经知道如果要去求出一个n个点、m条边的无向图的最小生成树有一个Krustal算法和另一个Prim的算法。另外,他还知道,某一个图可能有多种不同的最小生成树。例如,下面图 3中所示的都是图 2中的无向图的最小生成树:

     

    当然啦,这些都不是今天需要你解决的问题。Secsa想知道对于某一条无向图中的边AB,至少需要多少代价可以保证AB边在这个无向图的最小生成树中。为了使得AB边一定在最小生成树中,你可以对这个无向图进行操作,一次单独的操作是指:先选择一条图中的边 P1P2,再把图中除了这条边以外的边,每一条的权值都减少1。如图 4所示就是一次这样的操作:

    输入

    输入文件的第一行有3个正整数n、m、Lab分别表示无向图中的点数、边数、必须要在最小生成树中出现的AB边的标号。
    接下来m行依次描述标号为1,2,3…m的无向边,每行描述一条边。每个描述包含3个整数x、y、d,表示这条边连接着标号为x、y的点,且这条边的权值为d。
    输入文件保证1<=x,y<=N,x不等于y,且输入数据保证这个无向图一定是一个连通图。

    输出

    输出文件只有一行,这行只有一个整数,即,使得标号为Lab边一定出现最小生成树中的最少操作次数。

    样例输入

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

    样例输出

    1


    题解

    网络流最小割

    除了这条边以外其它边都-1,相当于其它边不变,这条边+1。

    然后考虑Kruscal求最小生成树的方法,一条边一定出现在最小生成树上,等价于所有边权小于等于它的边不能使得这两个端点连通。

    于是转化为最小割问题。

    对于每条长度小于等于给定的边,连容量为 给定长度-当前长度+1 的边,然后跑最小割即可。

    #include <queue>
    #include <cstdio>
    #include <cstring>
    #define N 510
    #define K 810
    #define M 100010
    using namespace std;
    queue<int> q;
    int x[K] , y[K] , z[K] , head[N] , to[M] , val[M] , next[M] , cnt = 1 , s , t , dis[N];
    void add(int x , int y , int z)
    {
        to[++cnt] = y , val[cnt] = z , next[cnt] = head[x] , head[x] = cnt;
        to[++cnt] = x , val[cnt] = z , next[cnt] = head[y] , head[y] = cnt;
    }
    bool bfs()
    {
        int x , i;
        memset(dis , 0 , sizeof(dis));
        while(!q.empty()) q.pop();
        dis[s] = 1 , q.push(s);
        while(!q.empty())
        {
            x = q.front() , q.pop();
            for(i = head[x] ; i ; i = next[i])
            {
                if(val[i] && !dis[to[i]])
                {
                    dis[to[i]] = dis[x] + 1;
                    if(to[i] == t) return 1;
                    q.push(to[i]);
                }
            }
        }
        return 0;
    }
    int dinic(int x , int low)
    {
        if(x == t) return low;
        int temp = low , i , k;
        for(i = head[x] ; i ; i = next[i])
        {
            if(val[i] && dis[to[i]] == dis[x] + 1)
            {
                k = dinic(to[i] , min(temp , val[i]));
                if(!k) dis[to[i]] = 0;
                val[i] -= k , val[i ^ 1] += k;
                if(!(temp -= k)) break;
            }
        }
        return low - temp;
    }
    int main()
    {
        int n , m , p , i , ans = 0;
        scanf("%d%d%d" , &n , &m , &p);
        for(i = 1 ; i <= m ; i ++ ) scanf("%d%d%d" , &x[i] , &y[i] , &z[i]);
        s = x[p] , t = y[p];
        for(i = 1 ; i <= m ; i ++ )
            if(i != p && z[i] <= z[p])
                add(x[i] , y[i] , z[p] - z[i] + 1);
        while(bfs()) ans += dinic(s , 1 << 30);
        printf("%d
    " , ans);
        return 0;
    }
    

     

  • 相关阅读:
    django之orm单表查询
    python通过os.system()方法执行pscp提示却找不到该应用程序
    VUE 条件编译
    博客园Silence新主题美化,2021年最新更新!换个口味~
    JavaScript中数组的操作方法总汇
    Vue 上传材料(使用input)
    postgresql关于array类型有交集(包含查询数据任意元素,有重叠&&)的一些查询方法以及sqlalchemy语句实现
    linux便捷日志查询操作
    安装RabbitMQ
    v-model语法糖在input上组件上的使用
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/7355935.html
Copyright © 2011-2022 走看看