Poj -1273 Drainage Ditches (网络流-ISAP)

题目链接

题意:给出m条路,n个点,每条路给出u,v,cap,求1~n的最大flow

题解:直接上ISAP,其实Dinic也可以.

        这里使用的是最大流-ISAP模板(ISAP + 当前弧优化 + GAP优化)   

代码如下:

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<algorithm>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
const int maxn = (int)1e5 + 500;
struct Edge {
	int v;//弧尾    
	int cap;
	int n;//指向下一条从同一个弧头出发的弧    
} edge[maxn];
int adj[maxn];//前向星的表头    
int Q[maxn], head, tail;//队列    
int d[maxn], cur[maxn], pre[maxn], num[maxn];
int nv;//编号修改的上限    
int top;
void add(int u, int v, int cap) {
	//正向边
	edge[top].v = v;
	edge[top].cap = cap;
	edge[top].n = adj[u];
	adj[u] = top++;
	//反向边    
	edge[top].v = u;
	edge[top].cap = 0;
	edge[top].n = adj[v];
	adj[v] = top++;
}
void rev_bfs(int t) {//反向BFS标号    
	memset(num, 0, sizeof(num));
	memset(d, -1, sizeof(d));//没标过号则为-1    
	d[t] = 0;//汇点默认为标过号    
	num[0] = 1;
	head = tail = 0;
	Q[tail++] = t;
	while (head != tail) {
		int u = Q[head++];
		for (int i = adj[u]; ~i; i = edge[i].n) {
			int v = edge[i].v;
			if (~d[v]) continue;//已经标过号    
			d[v] = d[u] + 1;//标号    
			Q[tail++] = v;
			num[d[v]]++;
		}
	}
}
int max_flow(int s, int t) {
	memcpy(cur, adj, sizeof(cur));//复制,当前弧优化    
	rev_bfs(t);//只用标号一次就够了,重标号在ISAP主函数中进行就行了    
	int flow = 0, u = pre[s] = s;
	while (d[t] < nv) {//最长也就是一条链,其中最大的标号只会是nv - 1,
					   //如果大于等于nv了说明中间已经断层了。  
		if (u == t) { //如果已经找到了一条增广路,则沿着增广路修改流量    
			int f = inf, neck;
			for (int i = s; i != t; i = edge[cur[i]].v) {
				if (f > edge[cur[i]].cap) {
					f = edge[cur[i]].cap;//不断更新需要减少的流量    
					neck = i;//记录回退点,目的是为了不用再回到起点重新找    
				}
			}
			for (int i = s; i != t; i = edge[cur[i]].v) { //修改流量  
				edge[cur[i]].cap -= f;
				edge[cur[i] ^ 1].cap += f;
			}
			flow += f;//更新    
			u = neck;//回退    
		}
		int i;
		for (i = cur[u]; ~i; i = edge[i].n)
			if (d[edge[i].v] + 1 == d[u] && edge[i].cap)
				break;
		if (~i) {//如果存在可行增广路,更新    
			cur[u] = i;//修改当前弧    
			pre[edge[i].v] = u;
			u = edge[i].v;
		}
		else { //否则回退,重新找增广路    
			if (0 == (--num[d[u]])) //GAP间隙优化,如果出现断层,可以知道
									//一定不会再有增广路了    
				break;
			int mind = nv;
			for (i = adj[u]; ~i; i = edge[i].n) {
				if (edge[i].cap && mind > d[edge[i].v]) {//寻找可以增广的最小标号  
					cur[u] = i;//修改当前弧    
					mind = d[edge[i].v];
				}
			}
			d[u] = mind + 1;
			num[d[u]]++;
			u = pre[u];//回退    
		}
	}
	return flow;
}
void init() {
	memset(adj, -1, sizeof(adj));
	top = 0;
}
int main() {
	int m, n;
	while (~scanf("%d%d", &m, &n)) {
		init();
		int u, v, w;
		for (int i = 0; i < m; ++i) {
			scanf("%d%d%d", &u, &v, &w);
			add(u, v, w);
		}
		nv = n + 1;
		printf("%d\n", max_flow(1, n));//这个是从1到n的
	}
	return 0;
}



 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值