zoukankan      html  css  js  c++  java
  • BZOJ2521 最小生成树 最小割

    5.26 T2:最小生成树

    Description

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

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

    Input

    输入文件的第一行有3个正整数n、m、Lab分别表示无向图中的点数、边数、必须要在最小生成树中出现的AB边的标号。

    接下来m行依次描述标号为1,2,3…m的无向边,每行描述一条边。每个描述包含3个整数x、y、d,表示这条边连接着标号为x、y的点,且这条边的权值为d。

    输入文件保证1<=x,y<=N,x不等于y,且输入数据保证这个无向图一定是一个连通图。

    Output

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

    Sample Input

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

    Sample Output

    1
    

    HINT

    第1个样例就是问题描述中的例子。

    1<=n<=500,1<=M<=800,1<=D<10^6

    Solution

    首先,除了一条边,所有边的权值-1等价于这条边的权值+1。然后我们回忆Kruskal的过程,这条边保证在最小生成树上,等价于:如果我们只加入权值<=这条边权值的边,该条边的两端点无法连通。那么直接转化成最小割问题,割掉每条边的代价=选定边权值-当前边权值+1即可。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    using namespace std;
     
    const int inf=0x7fffffff,maxn=501;
     
    int n,m,lab,tot,s,t,head[maxn],cur[maxn],dep[maxn],u[maxn<<1],v[maxn<<1],w[maxn<<1],to[maxn<<3],nxt[maxn<<3],val[maxn<<3];
     
    void add_edge(int u,int v,int w){
        nxt[++tot]=head[u];
        to[tot]=v;
        val[tot]=w;
        head[u]=tot;
    }
     
    bool bfs(){
        memset(dep,0,sizeof(dep));
        queue<int> q;q.push(s);
        dep[s]=1;
        while(!q.empty()){
            int u=q.front();q.pop();
            for(int i=head[u];i;i=nxt[i]){
                int v=to[i],w=val[i];
                if(!w or dep[v])continue;
                dep[v]=dep[u]+1;
                if(v==t)return 1;
                q.push(v);
            }
        }
        return 0;
    }
    int dfs(int u,int now){
        if(u==t or !now)
            return now;
        int ans=0;
        for(int &i=cur[u];i;i=nxt[i]){
            int v=to[i],w=val[i];
            if(dep[v]==dep[u]+1 and w){
                int dist=dfs(v,min(now,w));
                if(dist){
                    val[i]-=dist;
                    val[i^1]+=dist;
                    now-=dist;
                    ans+=dist;
                    if(!now)
                        break;
                }
            }
        }
        return ans;
    }
    int dinic(){
        int ans=0;
        while(bfs()){
            memcpy(cur,head,sizeof(head));
            int tmp;
            while(tmp=dfs(s,inf))ans+=tmp;
        }
        return ans;
    }
    int main(){
        scanf("%d%d%d",&n,&m,&lab);
        for(int i=1;i<=m;i++)
            scanf("%d%d%d",&u[i],&v[i],&w[i]);
        s=u[lab];t=v[lab];
        for(int i=1;i<=m;i++){
            if(i!=lab and w[i]<=w[lab]){
                add_edge(u[i],v[i],w[lab]-w[i]+1);
                add_edge(v[i],u[i],w[lab]-w[i]+1);
            }
        }
        printf("%d",dinic());
        return 0;
    }
    
    
  • 相关阅读:
    软件开发之需求调研方法论
    系统设计之编码概述
    unix/linux共享库(动态库)简介
    unix/linux静态库简介
    pytest的使用
    c语言中static的作用
    awk文本处理
    shell基本概念
    shell i/o交互及重定向
    shell常用分隔符及管道的用法
  • 原文地址:https://www.cnblogs.com/ezoihy/p/9113311.html
Copyright © 2011-2022 走看看