平时我们用的都是Dinic算法,每次bfs之后,我们习惯用dfs来找增广路。时间复杂度是。而且有时候很接近上界。
ISAP给这个Dinic算法带来了许多优化:
I表示的是improved,也就是说我们不用bfs那么多遍,我们可以通过每一次修改分层图的标号h来达到这个目的,怎么修改呢?
考虑一个点一开始可以到达的点,存在条件,其他有流量的边但是不可以到达的,说明一定在它之前到达了。
,k表示的是有流量但是不可以到达的点。如果到当前点x的流量不能被分完,那么说明需要更多点来满足到达x的流量。
这时,我们把中的最小值+1就可以达到我们的目的,因为总会加到
.特别的,当汇点的h等于n+1的时候,退出。(因为肯定有一个点被重复经过了两遍
前项弧优化,就是当x点的流量流完之后,下次过来的时候,可以从上次流量用尽的地方继续流,因为前面的流量必定为0.
这时我们需要做的就是bfs一遍,然后不断dfs了。
好像有点麻烦。
1.我们倒过来bfs,那么h记录的就是到end的最短距离,也就是说,会被出边的最小的
更新,这时如果我们在x点没有用尽流量,我们需要更多的点,我们就把
就好了,因为这时不能去到的点一定比
大。(仔细想想为什么
2.发现断层的时候,也是不行的。我们用一个gap来记录第i层的点有多少个,那么发现第i层的点从有变为没有时,就结束。
代码比Dinic要简短。
#include
#include
#include
#include
#include
#define inf 2147483647
using namespace std;
int n,m,begin,end;
struct edge{
int y,next,c;
}s[240010];
int first[10010],len=1,d[10010];
int h[10010];
int gap[10010];
queue f;
void ins(int x,int y,int c){
len++;
s[len]=(edge){y,first[x],c};h[x]=first[x]=len;
len++;
s[len]=(edge){x,first[y],0};h[y]=first[y]=len;
}
void bfs(){
++gap[d[end]=1];f.push(end);
while(!f.empty()){
int x=f.front();f.pop();
for(int i=h[x];i!=0;i=s[i].next){
int y=s[i].y;
if(d[y]==0) ++gap[d[y]=d[x]+1],f.push(y);
}
}
}
int dfs(int x,int t){
if(x==end) return t;
int my,tot=0;
for(int i=first[x];i!=0;i=s[i].next){
int y=s[i].y;
if(d[x]==d[y]+1 && s[i].c>0){
my=dfs(y,min(s[i].c,t-tot));
tot+=my;s[i].c-=my;s[i^1].c+=my;
}
if(t==tot) {
first[x]=i;
return t;
}
}
if(!(--gap[d[x]])) d[begin]=n+1;
++gap[++d[x]];first[x]=h[x];
return tot;
}
void Max_Flow(){
bfs();
int flow=dfs(begin,inf);
while(d[begin]<=n) flow+=dfs(begin,inf);
printf("%d\n",flow);
}
int main(){
scanf("%d %d %d %d",&n,&m,&begin,&end);
int x,y,c;
for(int i=1;i<=m;i++){
scanf("%d %d %d",&x,&y,&c);
ins(x,y,c);
}
Max_Flow();
}