最大流

基本知识:  流网络G = (V,E)是一个有向图,其中每条边有一非负容量c(u,v) >= 0。如果(u,v) 不属于E,则假定c(u,v) = 0。

       两个特殊点:源点:S  汇点: T              

         c:容量函数   f:实值函数(也就是最大流)

三个性质:   容量限制:对所有边,f(u,v) <= c(u,v);

      反对称性:对所有边,f(u,v) = - f(u,v);

      流守恒性:对除源点和汇点外的任意点,该点的流的为0。该点的流记为:|f|。

三种重要思想:  残留网络:Gf = (V,Ef) ,  其中边(u,v)的残留流量cf(u,v) = c(u,v) - f(u,v)  即可以再增加的流量值。

             Ef的建立:if 边(u,v) 属于 E

                    if f(u,v) < c(u,v)

                     then cf(u,v) = c(u,v) - f(u,v) > 0

                    if   f(u,v) > 0    so  f(v,u) < 0

                     then cf(v,u) = c(v,u) - f(v,u) > 0

                  else if 边(u,v)和(v,u)都不属于E   so c(u,v) = c(v,u) = 0,f(u,v) = f(v,u) = 0

                     then cf(u,v) = cf(v,u) = c(u,v) - f(u,v) = c(v,u) - f(v,u) = 0

                  SO   cf(u,v) = c(u,v) - f(u,v)

        增广路径:在Gf中一条从s到t的简单路径。最大流算法就是求增广路径,优化也是对这个进行优化。

        流网络的割G中割(S,T)将V划分成S和T=E-S两部分,使得s属于S,t属于T。

              f(S,T):穿过割(S,T)的净流,包括从S到T的流量,也包括从T到S的流量

              c(S,T):割(S,T)的容量,只包括从S到T的容量

              对所有割,其f都是相同的,但c是有大小的

              最小割:G中所有割中具有最小容量的割

最大流最小割定理:f是具有s和t的G中的一个流,下面三个定理等价:

         1:f是G的一个最大流

         2:Gf不包含增广路径

         3:对G中的某个割,有|f| = c(S,T)

Ford-Fulkerson方  为什么叫Ford-Fulkerson方法而不是算法,原因在于可以用多种方式实现这一方法,方式并不唯一。

           在每次迭代中,找出任意增广路径p,并把沿p每条边的流f加上其残留容留cf(p)。

 

Edmonds-Karp算法基于广度优先搜索(BFS)来计算增广路径P  时间复杂度O(V*E^2)

           算法流程如下:

            设队列Q--存储当前未检查的标号点,队首节点出队后,成为已检查的标点;

            path -- 存储当前已标号可改进路经;

            repeat

                   path置空;

                  源点s标号并进入path和Q;

                   while  Q非空  and  汇点t未标号 do

                          begin

                                 移出Q的队首顶点u;

                                 for 每一条从u出发的弧(u,v) do

                                        if  v未标号 and 弧(u,v)的流量可改进

                        then v进入队列Q和path;

                      end while

                    if 汇点已标号

                  then 从汇点出发沿着path修正可改进路的流量;

              until 汇点未标号;

   

  Edmonds-Karp算法有一个很重要的性质:当汇点未标号而导致算法结束的时候,那些已经标号的节点构成集合S,未标号的节点构成集合T,割(S,T)恰好是该流网络的最小割;且这样求出的最小割(S,T)中集合S的元素数目一定是最少的。

  BFS遍历增广路径代码:

HDU:1532 http://acm.hdu.edu.cn/showproblem.php?pid=1532

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

#define min(a,b) (a>b?b:a)

int n,m; //n条边,m个点
int Start,End;
queue
<int> Q;
const long maxn = 205;
const long inf = 1000000000;

int net[maxn][maxn]; //残留网络
int path[maxn]; //增广路径
int flow[maxn]; //增广路径的通过容量

void Init()
{
int i;
int from,to,val;

memset(net,
0,sizeof(net));

for(i=0;i<n;i++)
{
scanf(
"%d%d%d",&from,&to,&val);
net[from][to]
+= val;
}

Start
= 1;
End
= m;
//scanf("%d%d",&Start,&End);
}

int bfs()
{
//寻找增广路径
while(!Q.empty())
{
Q.pop();
}
memset(path,
-1,sizeof(path));

path[Start]
= 0;
flow[Start]
= inf;

Q.push(Start);
while(!Q.empty())
{
int top = Q.front();
Q.pop();

if(top == End)
break;

int i;
for(i=1;i<=m;i++)
{
if(i != Start && -1 == path[i] && net[top][i])
{
flow[i]
= min(flow[top],net[top][i]);
Q.push(i);
path[i]
= top;
}
}
}

if(-1 == path[End])
return -1;

return flow[m]; //为什么是m,不是End
}

void print(int ans)
{
printf(
"%d\n",ans);
}

void Edmonds_Karp()
{
int max_flow = 0;

int step;

while((step=bfs()) != -1)//不停的找增广路径,至到找不到
{
max_flow
+= step;

/*对残留网络进行修改*/
int now = End;
while(now != Start)
{
int pre = path[now];
net[pre][now]
-= step;
net[now][pre]
+= step;
now
= pre;
}
}

print(max_flow);
}

int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
Init();
Edmonds_Karp();
}
return 0;
}


Dinic算法:

 
 

自己对着网上写的一个代码,还很挫,用空再改改:

代码
#include <iostream>
#include
<queue>
#include
<stack>
using namespace std;

const long maxn = 205;
const long inf = 1000000000;

#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)

int m,n;
int start,end;
int map[maxn][maxn];
int level[maxn];
int vis[maxn];

void Init()
{
int i,a,b,c;
memset(map,
0,sizeof(map));
for(i=0;i<m;i++)
{
scanf(
"%d%d%d",&a,&b,&c);
map[a][b]
+= c;
}
start
= 1;
end
= n;
}

bool bfs()
{
int i;
memset(level,
-1,sizeof(level));

queue
<int> Q;
Q.push(start);
level[start]
= 0;

while(!Q.empty())
{
int top = Q.front();
Q.pop();

for(i=1;i<=n;i++)
{
if(level[i] == -1 && map[top][i])
{
level[i]
= level[top] + 1;
Q.push(i);
}
}
}

if(level[end] == -1)
return false;

return true;
}

int dfs(int ss,int limit)
{
int i;
if(ss == end)
return limit;

vis[ss]
= 1;

int flow = 0;
for(i=1;i<=n;i++)
{
if(level[i] - level[ss] == 1 && map[ss][i] && vis[i] == 0)
{
int temp = dfs(i,min(limit,map[ss][i]));
limit
-= temp;

map[ss][i]
-= temp;
map[i][ss]
+= temp;

flow
+= temp;

if(limit == 0)
break;
}
}
vis[ss]
= 0;

return flow;
}

int Dinic()
{
int max_flow = 0;
while(bfs())
{
memset(vis,
0,sizeof(vis));
int flow = dfs(start,inf);
if(flow == 0)
break;
max_flow
+= flow;
}
return max_flow;
}
int main()
{
while(scanf("%d%d",&m,&n)!=EOF)
{
Init();
int num = Dinic();

printf(
"%d\n",num);
}
return 0;
}

用邻接表写的

代码
#include <iostream>
using namespace std;

const long inf = 1000000000;
const long maxn_point = 205;
const long maxn_edge = 20005;

inline fmin(
int a,int b){return a<b?a:b;}
inline fmax(
int a,int b){return a>b?a:b;}

int n,m;//m条边,n个点
int start,end;
int Index;
struct node
{
int to;
int val;
int next;
}ditch[maxn_edge];
int pre[maxn_point];
int level[maxn_point],queue[maxn_point]; //层次图,模拟队列

inline
void single_insert(int from,int to,int cap)
{
ditch[Index].to
= to;
ditch[Index].val
= cap;
ditch[Index].next
= pre[from];
pre[from]
= Index++;
}
void insert(int from,int to,int cap)
{
single_insert(from,to,cap);
single_insert(to,from,
0);
}

void Init()
{
int a,b,c;
Index
= 0;
memset(pre,
-1,sizeof(pre));

while(m--)
{
scanf(
"%d%d%d",&a,&b,&c);
insert(a,b,c);
}
start
= 1;
end
= n;
}

bool bfs()
{
int i;
int ed=0,bg=0,x,y;
memset(level,
-1,sizeof(level));
level[start]
= 0;
queue[ed
++] = start;

while(bg<ed)
{
x
= queue[bg++];
for(i=pre[x];i+1;i=ditch[i].next)
{
y
= ditch[i].to;
if(ditch[i].val && level[y] == -1)
{
level[y]
= level[x] + 1;
if(y == end)//层次图不必要全部建完
return true;
queue[ed
++] = y;
}
}
}
return false;
}

int dfs(int x,int limit = inf)
{
int i,y,ret;
if(x == end)
return limit;

int flow = 0;
for(i=pre[x]; (i+1) && limit; i=ditch[i].next)
{
y
= ditch[i].to;
if(ditch[i].val && level[y] == level[x] + 1)
{
ret
= dfs(y,fmin(limit,ditch[i].val));

ditch[i].val
-= ret;
ditch[i
^1].val += ret;
limit
-= ret;
flow
+= ret;
}
}
return flow;
}

int Dinic()
{
int max_flow = 0;
while(bfs())
{
max_flow
+= dfs(start);
}

return max_flow;
}

int main()
{
while(scanf("%d%d",&m,&n)!=EOF)
{
Init();
int num = Dinic();
printf(
"%d\n",num);
}
return 0;
}


hdu:3526 http://acm.hdu.edu.cn/showproblem.php?pid=3526

设置超级源点和超级汇点,建立流向图。最大流做法

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

#define fmax(a,b) (a>b?a:b)
#define fmin(a,b) (a<b?a:b)

const long maxn_point = 505;
const long maxn_edge = 500050;
const long inf = 1e9;


int n,m,Index;
int start,end;
struct node
{
int to;
int val;
int next;
}com[maxn_edge];
int pre[maxn_point];
int level[maxn_point];

void _insert(int from,int to,int val)
{
com[Index].to
= to;
com[Index].val
= val;
com[Index].next
= pre[from];
pre[from]
= Index++;
}
void insert(int from,int to,int val)
{
_insert(from,to,val);
_insert(to,from,
0);
}

void Init()
{
int i,a,b,c;
Index
= 0;
start
= 0,end = n+1;
memset(pre,
-1,sizeof(pre));

for(i=1;i<=n;i++)
{
scanf(
"%d",&c);
insert(start,i,c);
}
for(i=1;i<=n;i++)
{
scanf(
"%d",&c);
insert(i,end,c);
}
for(i=0;i<m;i++)
{
scanf(
"%d%d%d",&a,&b,&c);
_insert(a,b,c);
_insert(b,a,c);
}
}

bool bfs()
{
int i;
memset(level,
-1,sizeof(level));

queue
<int> Q;
Q.push(start);
level[start]
= 0;

while(!Q.empty())
{
int x = Q.front();
Q.pop();

for(i=pre[x];(i+1);i=com[i].next)
{
int y = com[i].to;
if(level[y] == -1 && com[i].val)
{
level[y]
= level[x] + 1;
if(y == end)
return true;
Q.push(y);
}
}
}

return false;
}

int dfs(int x,int limit = inf)
{
int i;
if(x == end)
return limit;

int flow = 0,temp;
for(i=pre[x]; (i+1)&&limit; i=com[i].next)
{
int y = com[i].to;
if(com[i].val && level[y] == level[x] + 1)
{
temp
= dfs(y,fmin(limit,com[i].val));

com[i].val
-= temp;
com[i
^1].val += temp;

flow
+= temp;
limit
-= temp;
}
}

return flow;
}

int dinic()
{
int max_flow = 0;
while(bfs())
{
max_flow
+= dfs(start);
}

return max_flow;
}

int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
Init();
int num = dinic();
printf(
"%d\n",num);
}
return 0;
}

hdu:2063 http://acm.hdu.edu.cn/showproblem.php?pid=2063

建图的时候从女生到男生有容量,而从男生到女生那里没有的。

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

#define fmax(a,b) (a>b?a:b)
#define fmin(a,b) (a<b?a:b)

const long maxn_point = 1100;
const long maxn_edge = 10005;
const long inf = 1e9;

int Index,k,m,n;
int start,end;

struct node
{
int to;
int val;
int next;
}people[maxn_edge];
int pre[maxn_point];
int level[maxn_point];

void _insert(int from,int to,int val)
{
people[Index].to
= to;
people[Index].val
= val;
people[Index].next
= pre[from];
pre[from]
= Index++;
}

void insert(int from,int to,int val)
{
_insert(from,to,val);
_insert(to,from,
0);
}

void Init()
{
int i,a,b;
Index
= 0;

scanf(
"%d%d",&m,&n);
start
= 0,end = m+n+1;

memset(pre,
-1,sizeof(pre));

for(i=1;i<=m;i++)
{
insert(start,i,
1);
}
for(i=1;i<=n;i++)
{
insert(m
+i,end,1);
}
while(k--)
{
scanf(
"%d%d",&a,&b);
_insert(a,m
+b,1);
_insert(m
+b,a,0);
}
}

bool bfs()
{
int i;
queue
<int> Q;

Q.push(start);
memset(level,
-1,sizeof(level));
level[start]
= 0;

while(!Q.empty())
{
int x = Q.front();
Q.pop();

for(i=pre[x];(i+1);i=people[i].next)
{
int y = people[i].to;
if(people[i].val && -1 == level[y])
{
level[y]
= level[x] + 1;
if(y == end)
return true;
Q.push(y);
}
}
}

return false;
}

int dfs(int x,int limit = inf)
{
int i;
if(x == end)
return limit;

int flow = 0,temp;
for(i=pre[x]; (i+1) && limit; i=people[i].next)
{
int y = people[i].to;
if(people[i].val && level[y] == level[x] + 1 && (temp = dfs(y,fmin(limit,people[i].val))))
{
people[i].val
-= temp;
people[i
^1].val += temp;

limit
-= temp;
flow
+= temp;
}
}

return flow;
}
int dinic()
{
int max_flow = 0;
while(bfs())
{
//while(flow = dfs(start))
//max_flow += flow;
max_flow += dfs(start);
}

return max_flow;
}

int main()
{
while(scanf("%d",&k)!=EOF && k!=0)
{
Init();
int num = dinic();
printf(
"%d\n",num);
}
return 0;
}

SAP算法:从傻崽blog里拉来的。先当模板用!

邻接矩阵:

代码
#define M 1000
int maze[M][M];
int gap[M],dis[M],pre[M],cur[M];
int sap(int s,int t,int nodenum) {
CC(cur,
0);CC(dis,0);CC(gap,0);
int u = pre[s] = s,maxflow = 0,aug = -1;
gap[
0] = nodenum;
while(dis[s] < nodenum) {
loop: FOR(v,cur[u],nodenum)
if(maze[u][v] && dis[u] == dis[v] + 1) {
checkmin(aug,maze[u][v]);
pre[v]
= u;
u
= cur[u] = v;
if(v == t) {
maxflow
+= aug;
for(u = pre[u];v != s;v = u,u = pre[u]) {
maze[u][v]
-= aug;
maze[v][u]
+= aug;
}
aug
= -1;
}
goto loop;
}
int mindis= nodenum-1;
FF(v,nodenum)
if(maze[u][v] && mindis> dis[v]) {
cur[u]
= v;
mindis
= dis[v];
}
if((--gap[dis[u]])== 0) break;
gap[dis[u]
= mindis+1] ++;
u
= pre[u];
}
return maxflow;
}

邻接表:

代码
#define M 40010
int gap[M],dis[M],pre[M],cur[M];
int NE,NV;
int head[M];
struct Node{
int c,pos,next;
}E[
999999];
int sap(int s,int t) {
memset(dis,
0,sizeof(int)*(NV+1));
memset(gap,
0,sizeof(int)*(NV+1));
FF(i,NV) cur[i]
= head[i];
int u = pre[s] = s,maxflow = 0,aug = -1;
gap[
0] = NV;
while(dis[s] < NV) {
loop:
for(int &i = cur[u]; i != -1; i = E[i].next) {
int v = E[i].pos;
if(E[i].c && dis[u] == dis[v] + 1) {
checkmin(aug,E[i].c);
pre[v]
= u;
u
= v;
if(v == t) {
maxflow
+= aug;
for(u = pre[u];v != s;v = u,u = pre[u]) {
E[cur[u]].c
-= aug;
E[cur[u]
^1].c += aug;
}
aug
= -1;
}
goto loop;
}
}
int mindis = NV;
for(int i = head[u]; i != -1 ; i = E[i].next) {
int v = E[i].pos;
if(E[i].c && mindis > dis[v]) {
cur[u]
= i;
mindis
= dis[v];
}
}
if( (--gap[dis[u]]) == 0) break;
gap[ dis[u]
= mindis+1 ] ++;
u
= pre[u];
}
return maxflow;
}
void Insert(int u,int v,int c,int cc = 0) {
E[NE].c
= c; E[NE].pos = v;
E[NE].next
= head[u]; head[u] = NE++;
E[NE].c
= cc; E[NE].pos = u;
E[NE].next
= head[v]; head[v] = NE++;
}

int main() {
FF(i,NV) head[i]
= -1;
NE
= 0;
}
/**** **** **** **** **** ****
网络中最小费用最大流
参数含义: n代表网络中的总节点数
net[][]代表剩余网络
cost[][]代表单位费用
path[]保存增广路径
ecost[]源点到各点的最短路
算法:初始最小费用和最大流均为,寻找单位费用最短路
在最短路中求出最大流,即为增广路,再修改剩余网络,直到无可增广路为止
返回值: 最小费用,最大流量
**** **** **** **** **** ***
*/
const int NMAX = 210;
int net[NMAX][NMAX], cost[NMAX][NMAX];
int path[NMAX], ecost[NMAX];
int n;
bool bellman_ford()
{
int i,j;
memset(path,
-1,sizeof(path));
fill(ecost, ecost
+NMAX, INT_MAX);
ecost[
0] = 0;

bool flag = true;
while(flag) {
flag
= false;
for(i=0;i<=n;i++) {
if(ecost[i] == INT_MAX) {
continue ;
}
for(j=0;j<=n;j++) {
if(net[i][j] > 0 && ecost[i]+cost[i][j] < ecost[j]) {
flag
= true;
ecost[j]
= ecost[i]+cost[i][j];
path[j]
= i;
}
}
}
}
return ecost[n] != INT_MAX;
}

int min_cost_max_flow()
{
int i,j;
int mincost = 0, maxflow = 0;
while( bellman_ford() ) {
int now = n;
int neck = INT_MAX;
while(now != 0) {
int pre = path[now];
neck
= min(neck, net[pre][now]);
now
= pre;
}
maxflow
+= neck;
now
= n;
while(now != 0) {
int pre = path[now];
net[pre][now]
-= neck;
net[now][pre]
+= neck;
cost[now][pre]
= - cost[pre][now];
mincost
+= cost[pre][now] * neck;
now
= pre;
}
}
return mincost;
}
原文地址:https://www.cnblogs.com/silencExplode/p/1912189.html