最小割1


最小割1


概述

在最大流的定理我们证明了在最大流没有增广路,同时如果割的流量等于它的容量那么这个流是最大流。因此可以得出最大流是最小割。直接用dinic模板去求即可,然后如果从s点可以遍历到的点即为S集合,否则为T集合。


例题

给出一个带权无向图 G=(V,E)G=(V,E),每条边 e有一个权 we。

求将点 s 和点 t 分开的一个边割集 C,使得该割集的平均边权最小,即最小化:

∑(e∈C)we / |C|

注意: 边割集的定义与最小割中的割边的集合不同。在本题中,一个边割集是指:将这些边删去之后,ss 与 tt 不再连通。

输入格式

第一行包含四个整数 n,m,s,tn,m,s,t,其中 n,m 分别表示无向图的点数、边数。

接下来 mm 行,每行包含三个整数 a,b,w,表示点 a和 b 之间存在一条无向边,边权为 w。

点的编号从 1 到 n。

输出格式

输出一个实数,表示将点 s 和点 t 分开的边割集的最小平均边权。

结果保留两位小数。

先使用01分数规划做一个处理得到:∑(e∈C)(We - g)要证明这一部分最小值,直接使用二分去判断是否满足g;所以求当前图里的最小值,在每一次建图的时候,因为要使值最小,我们可以先把每一条边的容量-g,如果小于0那么我们直接选取这一条边即可,如果大于0,我们肯定只选择ST之间的容量,那么就是最小割。

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=110,M=810;
const double eps=1e-8,inf=1e8;
int n,m,S,T;
int head[N],idx;
int cur[N],q[N],d[N];
struct Edge{
    int next,to,w;
    double f;
}edge[M];
void add(int a,int b,int c)
{
    edge[idx]=(Edge){head[a],b,c,0},head[a]=idx++;
    edge[idx]=(Edge){head[b],a,c,0},head[b]=idx++;
}
bool bfs()
{
    int hh=0,tt=0;
    memset(d,-1,sizeof d);
    q[0]=S,d[S]=0,cur[S]=head[S];
    while(hh<=tt)
    {
        int t=q[hh++];
        for(int i=head[t];~i;i=edge[i].next)
        {
            int ver=edge[i].to;
            if(d[ver]==-1&&edge[i].f>0)
            {
                d[ver]=d[t]+1;
                cur[ver]=head[ver];
                if(ver==T)  return true;
                q[++tt]=ver;
            }
        }
    }
    return false;
}
double find(int u,double limit)
{
    if(u==T)    return limit;
    double flow=0;
    for(int i=cur[u];~i&&flow<limit;i=edge[i].next)
    {
        cur[u]=i;
        int ver=edge[i].to;
        if(d[ver]==d[u]+1&&edge[i].f>0)
        {
            double t=find(ver,min(edge[i].f,limit-flow));
            if(t<eps)   d[ver]=-1;
            edge[i].f-=t,edge[i^1].f+=t,flow+=t;
        }
    }
    return flow;
}
double dinic(double mid)
{
    double res=0;
    for(int i=0;i<idx;i+=2)
        if(edge[i].w<=mid) {
            res+=edge[i].w-mid;
            edge[i].f=edge[i^1].f=0;
        }
        else edge[i].f=edge[i^1].f=edge[i].w-mid;
    double r=0,flow;
    while(bfs())
        while(flow=find(S,inf))
            r+=flow;
    return r+res;
}
int main()
{
    memset(head,-1,sizeof head);
    scanf("%d%d%d%d",&n,&m,&S,&T);
    while(m--)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        add(a,b,c);
    }
    double l=0,r=1e7;
    while(r-l>eps)
    {
        double mid=(l+r)/2;
        if(dinic(mid)<eps)   r=mid;
        else    l=mid;
    }
    printf("%.2lf\n",r);
    return 0;
}


文章作者: Dydong
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Dydong !
  目录