最大流最小割C++实现

最大流量问题:寻找从源点到汇点的最大流量。

使用最短增益路径法(或先标记先扫描法)。这里标记意味着用两个记号来标记一个新的(为标记)的顶点:第一个标记指出从源点到被标记顶点还能增加多少流量;第二用来分别个标记指出另一个顶点的名字,就是从该顶点访问到被标记顶点的(对于源点来说这个标记可以不必指定)。方便起见,也可以为第二个标记加上“+”或“-”符号,用来分别指出该顶点是通过前向边还是后向边访问到的。因此,源点可被标记为∞,-。对于其他顶点,按照以下方法计算它的标记:

(1)如果为标记顶点j是由j到i的有向边和遍历队列中的前面顶点i相连接的,而且j具有大于0的未使用容量rij=uij-xij,其中uij为边的总容量,xij为当前正容量,那么顶点j就标记为lj,i+,其中lj=min{li,rij}。

(2)如果为标记顶点j是由j到i的有向边和遍历队列中的前面顶点i相连接的,而且j具有大于0的未使用容量xji,那么顶点j就标记为lj,i-,其中lj=min{li,xji}。

如果这种加入了标记的遍历结束于汇点,当前流量的增量就可以确定为汇点的第一个标记。我们从汇点开始,沿着第二个标记回溯到源点,来执行这一增量。在这条路径上,前向边的当前流量增加,后向边的当前流量减少。另一方面,如果遍历队列为空以后,汇点仍然没有被标记,该算法就把当前流量作为最大值返回并结束算法。

最小割

首先说明什么是割——我们可以把网络的顶点分成两个子集X和X‘,X包含源点,X‘是X的部,包含汇点。所有头在X‘,尾在X的边的集合称作,记作C(X,X‘)或C。

割C(X,X‘)容量,记作c(X,X‘),定义为构成割的边的容量和。最小割是具有最小容量的割。

最大流-最小割定理:网络中的最大流量值等于它的最小割的容量。

在最大流为问题的最后一次迭代时,所有已标记顶点到未标记顶点的边就构成了最小割。

注:以上文字摘自《算法设计与分析基础》,(美)Anany Levitin 著,潘彦 译。

#include<iostream>
#include<queue>
using namespace std;

const int MAX=100;
const int inf=1<<30;
queue<int> Q;

int ShortestAugmentingPath(int n,int source,int sink,int map[][MAX],int flow[][MAX],int pre_t[])
{
    /*  n为顶点数 ,source为源点,sink为汇点,map[][]记录对应边的最大容量,flow[][]记录对应边具有的正流量;
    最后一次迭代的pre_t[]记录了最小割,pre_t[i] !=0 表示属于源点集合  */

    if(source==sink)  //特殊情况处理
        return inf;

    int i,x;
    int visited[MAX];    //visited[i]=1表示顶点i被标记
    int rest[MAX][MAX];  //rest[i][j]表示顶点i到j的未使用流量
    int L[MAX];   //L[i]记录一次迭代过程中节点i的最大增量
    int pre[MAX]; //pre[i]=0表示顶点i未标记,pre[i] >0 表示前向边,pre[i] < 0表示后向边

   //数组初始化
    memset(rest,0,sizeof(rest));
    memset(visited,0,sizeof(visited));
    memset(flow,0,(n+1)*sizeof(flow[0]));
    memset(L,0x7f,n*sizeof(L[0]));
    memset(pre,0,sizeof(pre));   

    pre[source]=source+1;  //任意赋值为一个整数

    while(!Q.empty())
        Q.pop();
    Q.push(source);  //最初,源点入队

    while(!Q.empty())
    {
        visited[source]=1;  //源点被标记
        i=Q.front();
        Q.pop();

        for(x=1;x<=n;x++) //前向边
        {
            if(i!=x&&!visited[x]&&map[i][x]!=inf)
            {
                rest[i][x]=map[i][x]-flow[i][x];

                if( rest[i][x] > 0 )
                {
                    L[x]=L[i] < rest[i][x] ? L[i] : rest[i][x]; //L[x]=min(rest[i][x],L[i])
                    Q.push(x);
                    visited[x]=1;
                    pre[x]=i;
                }
            }
        }

        for(x=1;x<=n;x++)  //后向边
        {
            if(i!=x&&!visited[x]&&map[x][i]!=inf)
            {
                if(flow[x][i] > 0)
                {
                    L[x]=L[i] < flow[x][i] ? L[i] : flow[x][i];
                    Q.push(x);
                    visited[x]=1;
                    pre[x]=-i;
                }
            }
        }

        for(int y=1;y<=n;y++)  //记录最后一次迭代结果的标记,即为最小割
            pre_t[y]=pre[y];

        if(visited[sink]==1) //汇点被标记了
        {
            x=sink;
            while(x!=source) //从汇点开始用第二个标记反向移动
            {
                if(pre[x]>0)
                    flow[i][x] += L[sink];
                else
                    flow[i][x] -= L[sink];
                x=i;

                if(pre[i]>0 )
                    i=pre[i];
                else
                    i=-pre[i];
            }

            memset(visited,0,sizeof(visited)); //擦除标记
            memset(L,0x7f,n*sizeof(L[0]));

            while(!Q.empty())
                Q.pop();
            Q.push(source); //用源点对q重新初始化

            memset(pre,0,sizeof(pre));
            pre[source]=1;
        }
    }

    int maxflow=0;
    for(i=1; i <= n;i++)
        maxflow+=flow[i][sink];
    return maxflow;
}

int main()
{
    int map[MAX][MAX],flow[MAX][MAX],pre[MAX];
    int n,i,x,w;

    cout<<"输入顶点数:";
    while(cin>>n && n)
    {
        for(i=1;i<=n;i++)
        {
            for(x=1;x<=n;x++)
            map[i][x]=inf;
        }

        int m; //输入边数
        cout<<"输入边数:  ";
        cin>>m;

        cout<<"分别输入每个有向边的起点,终点和容量:"<<endl;
        while(m--)
        {
            cin>>i>>x>>w;
            map[i][x]=w;
        }
        cout<<"最大流是:"<<ShortestAugmentingPath(n,1,n,map,flow,pre)<<endl;

        cout<<"最小割为: 源点集合X={";
        for(i=1;i<=n;i++)
        {
            if(pre[i]!=0 )
                cout<<i<<" ";
        }
        cout<<"}, 汇点集合Y={";
        for(i=1;i<=n;i++)
        {
            if(pre[i]==0)
                cout<<i<<" ";
        }
        cout<<"}"<<endl;
    }
    return 0;
}

最大流最小割C++实现,布布扣,bubuko.com

时间: 2024-08-24 00:24:44

最大流最小割C++实现的相关文章

hihocoder 网络流二&#183;最大流最小割定理

网络流二·最大流最小割定理 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi:在上一周的Hiho一下中我们初步讲解了网络流的概念以及常规解法,小Ho你还记得内容么? 小Ho:我记得!网络流就是给定了一张图G=(V,E),以及源点s和汇点t.每一条边e(u,v)具有容量c(u,v).网络流的最大流问题求解的就是从s到t最多能有多少流量. 小Hi:那这个问题解决办法呢? 小Ho:解决网络流的基本思路就是寻找增广路,不断更新残留网络.直到找不到新的增广路,此时得到的

网络的最大流最小割定理

什么是流(flow): 在一个有向图中,只有出去的边没有进来的边的节点叫做源(source),只有进来的边没有出去的边的节点叫做汇(sink),其它的节点进来的边和出去的边应该是平衡的. 边上可以加权值,假设对于一个交通图来说,可以认为边上的权重为一条道路上的最大流量.那么对于图中任意两个节点来说,它们之间可以存在很多路径,每条路径上可以负载的最大流量应该是这条路径上权重最小的那条边所能承载的流量(联想一下“瓶颈”这个词,或者木桶理论),那么所有的路径上所负载流量之和也就是这两个节点之间多能通过

【codevs1907】方格取数3(最大流最小割定理)

网址:http://codevs.cn/problem/1907/ 题意:在一个矩阵里选不相邻的若干个数,使这些数的和最大. 我们可以把它看成一个最小割,答案就是矩阵中的所有数-最小割.先把矩阵按国际象棋棋盘黑白染色(即把相邻的点分别染成白色和黑色),然后黑点连源点,白点连汇点.割掉一个点到源/汇的边就是不选择这个点,最后的目的就是使源到汇不连通(不引发题目不能选择位置相邻的数的矛盾). 然而最小割怎么求呢? 于是我们就要引入一个定理:最大流最小割定理.它的意思就是说,在一个图中,a点到b点的最

poj3469(最大流最小割问题)

题目链接:http://poj.org/problem?id=3469 Dual Core CPU Time Limit: 15000MS   Memory Limit: 131072K Total Submissions: 18147   Accepted: 7833 Case Time Limit: 5000MS Description As more and more computers are equipped with dual core CPU, SetagLilb, the Chi

最大流最小割一题

a 看完题目,根本没想法,暴力的复杂度是指数级别的,枚举所有的集合,当时有点紧张,暴力的都没写,其实没思路的 时候最好写暴力的算法,骗点分就可以了.后来,看了牛客网上大神的思路,然后学习了下最大流最小割的方法,这题的 做法就是枚举源点和汇点,跑最大流算法,然后用流量更新答案,同时保存最小割,最后输出,就可以了. 简单写了下,也是无法实际运行去判断正误. 对最大流最小割的思路理解的不是很透彻,不知道怎么求最小割,没有做这方面的练习. 1 #include <iostream> 2 #includ

最大流最小割算法

图像分割中用到最小割原理,引出了最大流最小割算法,主要参考来自UCLA CIVS的Hong Chen的PPT <Introduction to Min-Cut/Max-Flow Algorithms> 一. 首先借用PPT的两张图分别讲述一下两者的应用 1. 最大流应用于网络传输 2. 最小割应用于桥问题 二.基本概念之 s-t 图 1. 源结点(source node)和汇结点(sink node) 2. 从结点i到结点j的有向边(Arc)<i, j> 3. 每条边<i,

网络流(三)最大流最小割定理

转载:https://blog.csdn.net/w417950004/article/details/50538948 割(CUT)是网络中顶点的划分,它把网络中的所有顶点划分成两个顶点的集合源点S和汇点T.记为CUT(S,T). 如下图:源点:s=1;汇点:t=5.框外是容量,框内是流量 如下图是一个图的割.顶点集合S={1,2,3}和T={4,5}构成一个割. 如果一条弧的两个顶点分别属于顶点集S和T那么这条弧称为割CUT(S,T)的一条割边.  从S指向T的割边是正向割边,从T指向S的割

hihoCoder1378 (最大流最小割)

#1378 : 网络流二·最大流最小割定理 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi:在上一周的Hiho一下中我们初步讲解了网络流的概念以及常规解法,小Ho你还记得内容么? 小Ho:我记得!网络流就是给定了一张图G=(V,E),以及源点s和汇点t.每一条边e(u,v)具有容量c(u,v).网络流的最大流问题求解的就是从s到t最多能有多少流量. 小Hi:那这个问题解决办法呢? 小Ho:解决网络流的基本思路就是寻找增广路,不断更新残留网络.直到找不到新的增

网络流基础-最大流最小割定理

最大流最小割定理,指网络流的最大流等于其最小割. 最大流指符合三个性质的前提下,从S到T能流过的最大流量. 最小割指符合割的定义,最小的割容量. 求最大流: 不断寻找增广路,计算能增加的最小流量,然后增加. 找到一条增光路,最多能流过2,则: 找到第二条路径: 最后还剩a-c-e一条,则可计算出最大流量为4. 但遇到以下情况,且第一条路径为a-b-c-d时,就不行了: 此时需要增加反向路径,即当减去增广路时,反向加上减去的流量,提供后悔的选择: 这样,当考虑a-c-b-d时,可以对冲掉b-c的流