基本知识: 流网络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 < 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 < 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 ;
}
用邻接表写的


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 < 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 < 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里拉来的。先当模板用!
邻接矩阵:


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;
}
邻接表:


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;
}