擴展:多路增廣
SAP(最短增廣路算法) 最大流模板
#include <iostream>
#include <queue>
#define msize 1024 //最大頂點數目
using namespace std;
int d[msize]; //標號
int r[msize][msize]; //殘留網絡,初始為原圖
int num[msize]; //num[i]表示標號為i的頂點數有多少
int pre[msize];
int n,m,s,t; //m個頂點,n條邊,從源點s到匯點t
void ini_d() //BFS計算標號,匯點t標號為0
{
int k;
queue<int>Q;
memset(d,1,sizeof(d));
memset(num,0,sizeof(num));
Q.push(t);
d[t]=0;
num[0]=1;
while (!Q.empty())
{
k=Q.front(),Q.pop();
for (int i=0;i<m;i++)
{
if (d[i]>=m&&r[i][k]>0)
{
d[i]=d[k]+1;
Q.push(i);
num[d[i]]++;
}
}
}
}
int findAlowArc(int i) //從i出發尋找允許弧
{
int j;
for (j=0;j<m;j++)
if (r[i][j]>0&&d[i]==d[j]+1) return j;
return -1;
}
int reLable(int i) //重新標號
{
int mm=INT_MAX;
for (int j=0;j<m;j++)
if (r[i][j]>0) mm=min(mm,d[j]+1);
return mm==INT_MAX?m:mm;
}
int maxFlow(int s,int t) //從源點s出發的最大流
{
int flow=0,i=s,j;
int delta; //增量
memset(pre,-1,sizeof(pre));
while (d[s]<m)
{
j=findAlowArc(i);
if (j>=0)
{
pre[j]=i;
i=j;
if (i==t) //更新殘留網絡
{
delta=INT_MAX;
for (i=t;i!=s;i=pre[i])
delta=min(delta,r[pre[i]][i]);
for (i=t;i!=s;i=pre[i])
r[pre[i]][i] -= delta, r[i][pre[i]] += delta;
flow += delta;
}
}
else
{
int x=reLable(i); //重新標號
num[x]++;
num[d[i]]–;
if (num[d[i]]==0) return flow; //間隙優化
d[i]=x;
if (i!=s) i=pre[i];
}
}
return flow;
}
FF算法
分為三個步驟,首先尋找一條增廣路(如果沒有增廣路,則返回最大流),同時保存路徑;其次,對路徑上的流量求最小值,記為min;最后根據路徑修改網絡,對於前向邊,減去最小值,對於后向邊,加上最小值,或者直接修改容量。
增廣路:從源點s到匯點t的一條有向路徑,如果從源點開始不可以到匯點,那么沒有增廣路。
保存:用一個數組保存,一般是采用父親表示法(父鏈接),保存當前節點的父親, 尋找的時候采用的是迭代的方式實現求最小值以及修改原網絡。
尋找增廣路,采用bfs的方式。詳細算法如下:
#include <iostream>
#include <queue>
using namespace std;
const int N = 210;
const int INF = 0x7FFFFFFF;
int n,m,map[N][N],path[N],flow[N],start,end;
queue<int> q;
int bfs()
{
int i,t;
while(!q.empty())
q.pop(); //清空隊列
memset(path,-1,sizeof(path)); //保存父親的數組,初始化為-1
path[start]=0,flow[start]=INF; //flow[] 保存當前的最小容量
q.push(start);
while(!q.empty())
{
t=q.front();
q.pop();
if(t==end) break; //找到一條增廣路
for(i=1;i<=m;i++)
{
if(i!=start && path[i]==-1 && map[t][i])
{
flow[i]=flow[t]<map[t][i]?flow[t]:map[t][i];
q.push(i);
path[i]=t;
}
}
}
if(path[end]==-1) return -1; //如果匯點的父親等於-1,說明不能到達最后。
return flow[m]; //一次遍歷之后的流量增量,min
}
int Edmonds_Karp()
{
int max_flow=0,step,now,pre;
while((step=bfs())!=-1)
{ //找不到增路徑時退出
max_flow+=step;
now=end;
while(now!=start)
{
pre=path[now];
map[pre][now]-=step; //更新正向邊的實際容量
map[now][pre]+=step; //添加反向邊
now=pre;
}
}
return max_flow;
}
int main()
{
int i,u,v,cost;
while(scanf("%d %d",&n,&m)!=EOF)
{
memset(map,0,sizeof(map));
for(i=0;i<n;i++)
{
scanf("%d %d %d",&u,&v,&cost);
map[u][v]+=cost; //not just on
}
start=1,end=m;
printf("%d\n",Edmonds_Karp());
}
return 0;
}
主要分為模塊三個:
模塊1:
int FF()
{
while(有增廣路){
更新原圖的前向邊(減min),后向邊(+min),同時保存最大流的增量值。
}
返回最大流值。
}
模塊2:
int agument_path()
{
源點進隊;
while(隊列不空)
{ 出隊;
如果達到匯點,則結束;
for(i=1;i<=m;i++){
//搜索整個圖的鄰接邊
如果未保存,且邊不為0(可增的量),且不是開始節點
進隊,保存,求最小值
}
如果匯點的父親沒有,說明沒有增廣路,返回-1;
返回最小值;
}
模塊3:
building{
for(i=0;i<n;i++){
scanf("%d %d %d",&u,&v,&cost);
map[u][v]+=cost; //not just on
}
最大流的建圖思想:對於每條邊,直接累加其容量就可以了。