[最大流模板ISAP] codevs 1993

     这道题目就是最大流的模板题,以前只会打Dinic,后来知道这个效率更高的算法后,匆忙学了一波。

     
          题目: http://codevs.cn/problem/1993/
   

        这道题目注意一下他两个点间可能有多条渠道,其他没什么陷阱,只要把两点间渠道的容量加起来就可以了。
  
        ISAP算法,他寻找最短增广路的方法与Dinic相同,区别在于一个是找不到增广路的时候重新构建分层图,而ISAP引入一个d(i)函数表示残量网络种结点i到汇点T的距离的最小值(下界),增广时,只允许沿着d(i)=d(j)+1的弧走,每当无法再增广下去时,修改d(i)=min{d(j)|(i,j)是残量网络中的弧}+1,然后往回走(此步骤被称为Retreat),如果残量网络中没有从i出发的弧了,就让d(i)=n,如果d(1)=n,那么就没有增广路了。
      ISAP算法中,还有一个优化,叫gap。用数组num[x]存储距离汇点T为x距离的结点个数,这样,当每次更新d(i)时,比如距离从x变为了y,那么num[x]--,num[y]++,如果num[x]为0了,那么说明s-t不连通,因为随着增广路的寻找,残量网络中的弧减少 ,d(i)只增不减,即y>x,则x把图分成各一半,也就是说如果num[x]为0,说明num[x+1]以上的点与num[x-1]一下的点之间是不连通的,好比一列轨道,中间少了一轨,就成了两段。自己好好体会体会一下,应该挺容易明白的。
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
#define max_N 210
#define max_M 210
#define INF 2147483647
using namespace std;
int n,m;
int d[max_M],p[max_M],cur[max_M],num[max_M];
int cap[max_M][max_M],flow[max_M][max_M];
int x,y,zhi,tot;
bool vis[max_M];
bool bfs(){
	memset(vis,0,sizeof(vis));
	for(int i=1;i<=m;i++) d[i]=n;
	queue <int> que;
	que.push(m);
	vis[m]=1;
	d[m]=0;
	while(!que.empty()){
		int u=que.front();que.pop();
		for(int i=1;i<=m;i++){
			if(i==u) continue;
			if(cap[i][u]&&!vis[i]){
			d[i]=d[u]+1;
			vis[i]=1;
			que.push(i);
		    }
		}
	}
	return vis[1];
}

int Augment(){
	int x=m,a=INF;
	while(x!=1){
		a=min(a,cap[p[x]][x]-flow[p[x]][x]);
		x=p[x];
	}
	x=m;
	while(x!=1){
		flow[p[x]][x]+=a;
		flow[x][p[x]]-=a;
		x=p[x];
	}
	return a;
}
int Maxflow(int s,int t){
	if(!bfs())return 0;
	int Flow=0,x=1;
	memset(num,0,sizeof(num));
	memset(cur,0,sizeof(cur));
	for(int i=0;i<=m;i++)num[d[i]]++;
	while(d[s]<m){
		if(x==m){
			Flow+=Augment();
			x=s;
		}
		bool ok=0;
		for(int i=cur[x];i<=m;i++){
			if(i==x||i==0) continue;
			if(cap[x][i]>flow[x][i]&&d[x]==d[i]+1){    //Advance
				ok=1;
				p[i]=x;
				cur[x]=i;
				x=i;
				break;
			}
		}
		if(!ok){    //retreat
		int ans=m-1;
		 for(int i=1;i<=m;i++){
		 	if(i==x) continue;
		 	if(cap[x][i]>flow[x][i])ans=min(ans,d[i]);
		 }
		 if(--num[d[x]]==0) break;         //gap优化 
		 num[d[x]=ans+1]++;
		 cur[x]=1;
		 if(x!=s) x=p[x];
		}
	}
	return Flow;
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%d%d%d",&x,&y,&zhi);
		if(x==y) continue;
		cap[x][y]+=zhi;		
	}
	printf("%d",Maxflow(1,m));
	return 0;
}

下面贴代码:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值