1. 程式人生 > >學習筆記第三十一節:ISAP

學習筆記第三十一節:ISAP

正題

      平時我們用的都是Dinic演算法,每次bfs之後,我們習慣用dfs來找增廣路。時間複雜度是O(n^2*m)。而且有時候很接近上界。

       ISAP給這個Dinic演算法帶來了許多優化:

    第一個

       I表示的是improved,也就是說我們不用bfs那麼多遍,我們可以通過每一次修改分層圖的標號h來達到這個目的,怎麼修改呢?

       考慮一個點一開始可以到達的點,存在條件h[x]+1=h[y],其他有流量的邊但是不可以到達的,說明一定在它之前到達了。

        h[k]<h[x]+1,k表示的是有流量但是不可以到達的點。如果到當前點x的流量不能被分完,那麼說明需要更多點來滿足到達x的流量。

        這時,我們把h[k]中的最小值+1就可以達到我們的目的,因為總會加到h[x]+1.特別的,當匯點的h等於n+1的時候,退出。(因為肯定有一個點被重複經過了兩遍

    第二個

        前項弧優化,就是當x點的流量流完之後,下次過來的時候,可以從上次流量用盡的地方繼續流,因為前面的流量必定為0.

       這時我們需要做的就是bfs一遍,然後不斷dfs了。

       好像有點麻煩。

     小小的優化和程式碼簡寫方法:

       1.我們倒過來bfs,那麼h記錄的就是到end的最短距離,也就是說,h[x]會被出邊的最小的h[y]更新,這時如果我們在x點沒有用盡流量,我們需要更多的點,我們就把h[x]++就好了,因為這時不能去到的點一定比h[x]大。(仔細想想為什麼

       2.發現斷層的時候,也是不行的。我們用一個gap來記錄第i層的點有多少個,那麼發現第i層的點從有變為沒有時,就結束。

       程式碼比Dinic要簡短。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<queue>
#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<int> 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();
}