不停地找增广路进行增广,知道找不到增广路为止。
每一次bfs只找一条增广路。
时间复杂度 O(VE2)
// codevs 1993
#include
#include
#include
#include
using namespace std;
const int inf=2100000000;
int n,m,maxflow,a[205][205],flow[205],pre[205];
//n表示边数,m表示点数,maxflow为最大流流量,a为每条边的容量,flow为每个点增广的流量,pre为
增广时点的前驱
int x,y,cap;
queue <int> q;
inline int bfs(int s,int t){
while (!q.empty()) q.pop();
for (int i=1;i<=m;++i) pre[i]=-1;
pre[s]=0;
q.push(s);
flow[s]=inf;
while (!q.empty()){
int x=q.front();
q.pop();
if (x==t) break;
for (int i=1;i<=m;++i)
//EK一次只找一个增广路
if (a[x][i]>0&&pre[i]==-1){
pre[i]=x;
flow[i]=min(flow[x],a[x][i]);
q.push(i);
}
}
if (pre[t]==-1) return -1;
else return flow[t];
}
//s为源点,t为汇点
//increase为增广的流量
inline void ek(int s,int t){
int increase=0;
while ((increase=bfs(s,t))!=-1){
int k=t;
while (k!=s){
int last=pre[k];
a[last][k]-=increase;
a[k][last]+=increase;
k=last;
}
maxflow+=increase;
}
}
int main(){
scanf("%d%d",&n,&m);
for (int i=1;i<=n;++i){
scanf("%d%d%d",&x,&y,&cap);
a[x][y]+=cap;
}
ek(1,m);
printf("%d\n",maxflow);
}
dinic算法在EK算法的基础上增加了分层图的概念,根据从s到各个点的最短距离的不同,把整个图分层。寻找的增广路要求满足所有的点分别属于不同的层,且若增广路为 s,P1,P2…Pk,t ,点 v 在分层图中的所属的层记为 deepv ,那么应满足 deeppi=deeppi−1+1
在普通情况下, DINIC算法时间复杂度为 O(V2E)
在二分图中, DINIC算法时间复杂度为 O(V−−√E)
• 多路增广
每次不是寻找一条增广路,而是在DFS中,只要可以就递归增广下去,实际上形成了一张增广网。
• 当前弧优化
对于每一个点,都记录上一次检查到哪一条边。因为我们每次增广一定是彻底增广(即这条已经被增广过的边已经发挥出了它全部的潜力,不可能再被增广了),下一次就不必再检查它,而直接看第一个未被检查的边。
优化之后渐进时间复杂度没有改变,但是实际上能快不少。
实际写代码的时候要注意,next数组初始值为-1,存储时从0开始存储,这样在后面写反向弧的时候比较方便,直接异或即可。
// codevs 1993
#include
#include
#include
#include
using namespace std;
const int max_n=205;
const int max_m=205;
const int max_e=max_m*2;
const int inf=1e9;
int point[max_n],next[max_e],v[max_e],remain[max_e],deep[max_n],cur[max_n];
int n,m,x,y,cap,tot,maxflow;
queue <int> q;
inline void add(int x,int y,int cap){
++tot; next[tot]=point[x]; point[x]=tot; v[tot]=y; remain[tot]=cap;
++tot; next[tot]=point[y]; point[y]=tot; v[tot]=x; remain[tot]=0;
}
//分层
inline bool bfs(int s,int t){
//初始化
memset(deep,0x7f,sizeof(deep));
deep[s]=0;
for (int i=1;i<=n;++i)
cur[i]=point[i];
while (!q.empty()) q.pop();
q.push(s);
while (!q.empty()){
int now=q.front(); q.pop();
for (int i=point[now];i!=-1;i=next[i])
if (deep[v[i]]>inf&&remain[i]){
deep[v[i]]=deep[now]+1;
q.push(v[i]);
}
}
return deep[t]//找到当前点最大能够增广的flow
//limit表示到目前为止走过的增广路容量最小的边
inline int dfs(int now,int t,int limit){
if (!limit||now==t) return limit;
int flow=0,f;
for (int i=cur[now];i!=-1;i=next[i]){
cur[now]=i;
if (deep[v[i]]==deep[now]+1&&(f=dfs(v[i],t,min(limit,remain[i])))){
flow+=f;
limit-=f;
remain[i]-=f;
remain[i^1]+=f;
if (!limit) break;
}
}
return flow;
}
inline void dinic(int s,int t){
while (bfs(s,t))
maxflow+=dfs(s,t,inf);
}
int main(){
tot=-1;
memset(point,-1,sizeof(point));
memset(next,-1,sizeof(next));
scanf("%d%d",&m,&n);
for (int i=1;i<=m;++i){
scanf("%d%d%d",&x,&y,&cap);
add(x,y,cap);
}
dinic(1,n);
printf("%d\n",maxflow);
}
ISAP(ImprovedShortestAugmentingPath)也是基于分层思想的最大流算法。所不同的是,它省去了
Dinic每次增广后需要重新构建分层图的麻烦,而是在每次增广完成后自动更新每个点的标号(也就是所
在的层)
前进到 j 点。
3. 没有满足条件的出边,对 i 点重新进行标号。 deepi=min{deepj|(i,j)∈E′,c′ij>0} ,其
中 E′ 为残量网络的边集。
算法结束的条件: deeps=+∞ (类似于Dinic,即在残量网络中已不存在s到t的通路。
渐进时间复杂度和dinic相同,但是非二分图的情况下isap更具优势。
1、当前弧优化:和dinic相同
2、GAP优化:
numk 记录当前有多少点编号为 k
若 ∃k∈[0,deeps],numk=0 ,则说明当前的网络不可能再被增广,可以直接退出。
// codevs 1993
#include
#include
#include
#include
using namespace std;
const int max_n=205;
const int max_m=205;
const int max_e=max_m*2;
const int inf=1e9;
int point[max_n],next[max_e],v[max_e],remain[max_e],tot;
int cur[max_n],deep[max_n],last[max_n],num[max_n];
int n,m,x,y,cap,maxflow;
queue <int> q;
inline void add(int x,int y,int cap){
++tot; next[tot]=point[x]; point[x]=tot; v[tot]=y; remain[tot]=cap;
++tot; next[tot]=point[y]; point[y]=tot; v[tot]=x; remain[tot]=0;
}
inline void bfs(int t){
for (int i=1;i<=n;++i)
deep[i]=n;
deep[t]=0;
while (!q.empty()) q.pop();
q.push(t);
while (!q.empty()){
int now=q.front(); q.pop();
for (int i=point[now];i!=-1;i=next[i])
if (deep[v[i]]==n&&remain[i^1]){
deep[v[i]]=deep[now]+1;
q.push(v[i]);
}
}
}
inline int addflow(int s, int t) {
int ans=inf,now=t;
while (now!=s) {
ans=min(ans, remain[last[now]]);
now=v[last[now] ^ 1];
}
now=t;
while (now != s) {
remain[last[now]]-=ans;
remain[last[now]^1]+=ans;
now=v[last[now]^1];
}
return ans;
}
inline void isap(int s,int t){
int now=s;
bfs(t);
for (int i=1;i<=n;++i) ++num[deep[i]];
for (int i=1;i<=n;++i) cur[i]=point[i];
//在残量网络中没有源点到汇点的通路
while (deep[s]//如果到达汇点则进行增广,重新回到源点准备下一轮增广
if (now==t){
maxflow+=addflow(s,t);
now=s;
}
bool has_find=false;
//当前弧优化
for (int i=cur[now];i!=-1;i=next[i]){
int u=v[i];
if (deep[u]+1==deep[now]&&remain[i]){
has_find=true;
cur[now]=i;
last[u]=i;
now=u;
break;
}
}
//没有找到出边,重新进行标号
if (!has_find){
int minn=n-1;
for (int i=point[now];i!=-1;i=next[i])
if (remain[i])
minn=min(minn,deep[v[i]]);
//GAP优化
if (!(--num[deep[now]])) break;
num[deep[now]=minn+1]++;
cur[now]=point[now];
if (now!=s)
now=v[last[now]^1];
}
}
}
int main(){
tot=-1;
memset(point,-1,sizeof(point));
memset(next,-1,sizeof(next));
scanf("%d%d",&m,&n);
for (int i=1;i<=m;++i){
scanf("%d%d%d",&x,&y,&cap);
add(x,y,cap);
}
isap(1,n);
printf("%d\n",maxflow);
}