最大网络流——增广路算法

几句废话:
读了刘汝佳的书之后,感觉一切都是那么茫然,于是自己在网上找教程,自己一点点码的,大概用了三天。
网络流基础:
看来我很有必要说一下网络流的基础
网络流问题就是给你一个图,每个图的边权叫做这条边的流量,问你从起始点出发,有多少值能通过这些边流到重点
我知道你没看懂,举个例子:

如图:

最大值为

从1到2到4运6个

从1到2到3到4运1个

从1到3到4运3个

一共运10个。

举例说完了,那么我说几个定义:

容量,就只一条边的权值,表示能从这条边运送的最大值

流量,表示一条边实际上流过的最大值

那么,说算法的时间到了,还是先上数据

(数据为上图所示)

4 5//四个点,五条边
1 2 8
1 3 3
2 3 1
2 4 6
3 4 5

开始,我们假设所有边的流量都是0

以这个数据,这样可以达到。

于是我们试图增加一些变得流量,使得重点的流量更大。

那么如何增加?就需要我们找増广路。

什么是増广路,就是一条从起点,到终点的一条每条边容量-实际流量>的路0。

比如

0/8表示一条边流量为0,容量为8

现在很明显可以看出,存在一条増广路1——2——4

然后怎么办,把这条路上的每条边流量都加上每条边容量与流量差的最小值

例如8-0>6-0 因此1——2、2——4这两条边的流量都加上6,并且答案也加上6

然后我们继续找増广路,又发现一条1——2——3——4

还是按刚才的,找出最小值,为1,所以这条增光路上每一边流量加1

然后ans再加一变成7

然而我们继续找増广路,发现1——3——4

按照刚才,流量加上2,变成

ans加上3变成10

然后发现没有増广路了,于是算法结束,答案是10.

等等,这就要上代码了?传说中得noi算法网络流就这么简单?不存在的

细心的同学(滑稽)会发现,这种情况就很神奇

你可能回算1——2——3——4这条増广路,于是答案是1,但是事实证明最优答案明显是2,那么问题出在哪里?

因为我们没有给予返回的机会,也就是相当于第一次找到的不是最优解,那么怎么办?

所以,我们要有一个反向边,来给程序反悔的机会,每条边都创建一条反向边,反向边的初始容量是0,流量都为负数。

假设1——2这条边本来权值是1,流量也是1,那么他的反向边的容量是0,流量是-1,这个应该好理解。

上图就会变为下图:

然后以刚才举例,当确定増广路1——2——3——4后,图是这样的:

于是,我们又找到了一条新的増广路:1——3——2——4

(因为0-(-1)=1,所以这条边也可以走)

那么答案是1+1=2

那么问题来了,为什么这样就算一种呢?

因为制造相反边,如2——3,就是相当于吧原来从而流到3的量流回来了。

这样就可以求出最大值。

口胡内容就到这里,其实我写的不是很清楚,大家看刘汝佳的可能会更清楚一些,但是重点来了!!!

这道题代码实现非常之困难,至少对于我来说,所以刘汝佳的代码我压根没看懂。

因此,我自己写得一份浅显易懂的代码

没有vector!!!没有指针!!!

而且有复杂的注释!!!

#include <iostream>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <queue>
using namespace std;
int n,m;//n个点,m条边
int head[100010];//这是邻接表的标志,head[i]表示以i为顶点的第一条边
struct edge
{
    int cap,flow;//cap为容量,flow为流量
    int from,to;//一条边的起点终点
    int next;//和这条边起点相同的下一条边(邻接表标志)
}map[100010];
int index=-1;//邻接表输入数组
int flag=0;//退出的标记
void build_edge(int a,int b,int c)//构造邻接表,插入连接表
{
    index++;
    map[index].from=a;
    map[index].to=b;
    map[index].cap=c;
    map[index].flow=0;
    map[index].next=head[a];
    head[a]=index;
    index++;
    map[index].from=b;//插入相反边
    map[index].to=a;
    map[index].cap=0;
    map[index].flow=0;
    map[index].next=head[b];
    head[b]=index;
    return ;
}
int bfs()//运用bfs找到増广路
{
    int min_flow[100010];//min_flow[i]代表到第i好点时,当前所走过变的容量-流量的最小值
    memset(min_flow,0,sizeof(min_flow));
    queue<int> Q;//用队列 维护
    min_flow[1]=999999;//开始为无限大
    int p[100010];//这个很重要,构造增光路时,记录当前点是由那条边找到的,以此找到増广路时能从终点以此到起点的边的流量加上最小值
    p[1]=-1;
    Q.push(1);//起点入队
    while(!Q.empty())
    {
        int now=Q.front();
        Q.pop();
        for(int e=head[now];e!=-1;e=map[e].next)//邻接表枚举当前点的所有连边
        {
            int v=map[e].cap-map[e].flow;//v就是为当前边容量-流量
            if(v>0 && !min_flow[map[e].to])//当v>0 并且这个点没有访问过时
            {
                p[map[e].to]=e;//记录到达这个点的边的序号
                min_flow[map[e].to]=min(min_flow[now],v);//维护最小值
                Q.push(map[e].to);//维护bfs
            }
        }
        if(min_flow[n]!=0)//如果终点得到更新,说明找到増广路,直接break
            break;
    }
    if(min_flow[n]==0)//当没有更新到终点,也就是重点最小增加值为0时,说明没有増广路了,直接完事
        flag=1;
    for(int e=n;;e=map[p[e]].from)//从终点开始,以此倒着走増广路,把沿途上边的流量都加上终点最小更新值
    {
        if(p[e]==-1)//如果到头就完事
            break;
         map[p[e]].flow+=min_flow[n];//流量加上最小更新至
         map[p[e]^1].flow-=min_flow[n];//反向边减去
    }
    return min_flow[n];//返回最小更新至,加到答案当中
}
int max_flow()
{
    int flow=0;//这就是神圣的答案
    while(1)
    {
        flow+=bfs();//循环搜索
        if(flag==1)//没有増广路,就直接跳出
            break;
    }
    return flow;//返回答案
}
int main()
{
    cin>>n>>m;
    for(int i=0;i<=m;i++)//初始化,很重要,制胜之点
    {
        map[i].next=-1;
        head[i]=-1;
    }
    for(int i=1;i<=m;i++)//读入,构造邻接表
    {
        int a,b,c,d;
        cin>>a>>b>>c;
        build_edge(a,b,c);
    }
    cout<<max_flow();//输出答案
}
/*
测试数据
4 4
1 2 2
2 4 2
1 3 3
3 4 1
*/

学信息不易,作业还没动,求关注!!!

时间: 2024-08-16 20:13:35

最大网络流——增广路算法的相关文章

网络流——增广路算法(dinic)模板

1 #include<iostream> 2 #include<cstring> 3 #include<algorithm> 4 #include<cmath> 5 #include<cstdio> 6 #include<queue> 7 using namespace std; 8 struct data 9 { 10 int from,to,next,cup,flow; 11 data(){from=-1,to=-1,next=-

最大流——增广路算法

关于网络流的增广路算法,网上有很多详细介绍,这里是摘录的模板.详细请见:http://www.cnblogs.com/kuangbin/archive/2011/07/26/2117636.html 1 #include<iostream> 2 #include<iomanip> 3 #include<ctime> 4 #include<climits> 5 #include<algorithm> 6 #include<queue>

关于最短增广路算法和连续最短增广路算法的操作步骤

最短增广路算法(SAP): 1.初始化容量网络和网络流: 2.构造残留网络和层次网络,如果汇点不在层次网络中,则算法结束: 3.在层次网络中不断用BFS增广,直到层次网络中没有增广路为止:每次增广完毕,在层次网络中要去掉因改进流量而导致饱和的弧: 4.转到步骤(2). 连续最短增广路算法(Dinic): 1.初始化容量网络和网络流: 2.构造残留网络和层次网络,如果汇点不在层次网络中,则算法结束: 3.在层次网络中用一次DFS过程进行增广,DFS执行完毕,该阶段的增广也执行完毕: 4.转到步骤(

Power Network(最大流基础_增广路算法:多源多汇,自建总源点和总汇点)

 Power NetworkCrawling in process... Crawling failed Time Limit:2000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit Status Description A power network consists of nodes (power stations, consumers and dispatchers) connected by p

Drainage Ditches(最大流基础_增广路算法)

 Drainage DitchesCrawling in process... Crawling failed Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit Status Description Every time it rains on Farmer John's fields, a pond forms over Bessie's favorite clover p

增广路算法

#include<queue> #include<cstdio> #include<iostream> #include<cstring> using namespace std; const int maxn = 20; const int INF = (1<<30); int cap[maxn][maxn],flow[maxn][maxn];//cap记录容量,flow记录流量 int m; //弧的数量 int EdmondsKarp(in

网络流 之 一般增广路算法 标号法实现

数据输入格式:首先输入顶点个数n和弧数m,然后输入每条弧的数据.规定源点为顶点0,汇点为顶点n-1.每条弧的数据格式为:u,v,c,f,分别表示这条弧的起点,终点,容量和流量.顶点序号从0开始. 代码: 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <cmath> 6 #include <str

网络流之 最短增广路算法模板(SAP)

数据输入格式:首先输入顶点个数n和弧数m,然后输入每条弧的数据.规定源点为顶点0,汇点为顶点n-1.每条弧的数据格式为:u,v,w,分别表示这条弧的起点,终点,容量.顶点序号从0开始. 代码: 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <cmath> 6 #include <string&g

网络流初步——增广路算法(EK)模板

1 #include <iostream> 2 #include <queue> 3 #include<string.h> 4 using namespace std; 5 #define arraysize 201 6 int maxData = 0x7fffffff; 7 int a[222][222]; 8 int flow[222]; 9 int pre[222]; 10 int n,m; 11 queue<int> q; 12 int BFS(in