有源汇上下界最大流

有源汇上下界最大流

只记录一下做法,原理不证明。。

有源汇上下界最大流,指的是

n n n个点, m m m条边,每条边 e e e有一个流量下界 l o w e r lower lower和流量上界 u p p e r upper upper,给定源点 s s s和汇t点,求源点到汇点的最大流

(相当于在之前的最大流的基础上,每条边加上一个流量下界,来求最大流)

做这个题之前先来看这样一条道题

无源汇有上下界可行流

n n n个点, m m m条边,每条边 e e e有一个流量下届 l o w e r lower lower和流量上界 u p p e r upper upper,求一种可行方案使得在所有点满足流量平衡条件的前提下,所有边满足流量限制。

输入

第一行两个正整数 n 、 m n、m nm

之后的 m m m行,每行四个整数 s , t , l o w e r , u p p e r s,t,lower,upper s,t,lower,upper

输出

如果无解输出一行 N O NO NO,否则第一行输出 Y E S YES YES,之后 m m m行每行一个整数,表示每条边的流量

样例输入

4 6
1 2 1 3
2 3 1 3
3 4 1 3
4 1 1 3
1 3 1 3
4 2 1 3

样例输出

YES
1
2
3
2
1
1

由于每一条弧的下界是必须要满足的流量限制,因此可以把这条弧分出一条必要弧出来,权值就是下界的流量大小

如图

将下界作为必要弧分离出来,原来的弧的权值为 u p − l o w up-low uplow

接着建立一个源点 s s s,一个汇点 t t t,对于一个必要弧 ( u , v ) (u,v) (u,v)使用源点和汇点串成 ( u , t ) (u,t) (u,t) ( s , v ) (s,v) (s,v),这样可以建立一个等价网络

接着就是最大流算法,求从 s s s t t t求最大流即可,但是需要注意的是

  • 必要弧必须流满,也就是如果求出来的最大流 m a x f l o w &lt; ∑ i = 1 m l o w e r ( i ) maxflow &lt; \sum_{i=1}^{m}lower(i) maxflow<i=1mlower(i),则不存在解。
  • 求出最大流后,用原来的网络减去算法跑完最大流后的网络,就是要求的可行流

代码

#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
#define N 205
#define M 10205
#define INF 0x7fffffff
using namespace std;

inline int min(int a, int b) {
	if(a > b) return b;
	else return a; 
}
struct Edge {
	int to, next, cap, up; //其中的up用来标记初始网络的边的值
	Edge () {
		to = next = cap = up = 0;
	}
}edge[M * 10];

int head[N], cur[N], dep[N];
int n, m, s, t, cnt = -1, maxflow;
queue <int> q;

void addEdge (int u, int v, int cost) {
	edge[++cnt].to = v;
	edge[cnt].cap = cost;
	edge[cnt].next = head[u];
	head[u] = cnt;
}

bool bfs () {
	while(!q.empty()) q.pop();
	for(int i = 0; i <= n+1; i++) cur[i] = head[i], dep[i] = -1;
	dep[s] = 0;
	q.push(s);
	while(!q.empty()) {
		int x = q.front(); q.pop();
		for(int i = head[x]; i != -1; i = edge[i].next) {
			int v = edge[i].to, cap = edge[i].cap;
			if(dep[v] == -1 && cap > 0) {
				dep[v] = dep[x] + 1;
				q.push(v);
			}
		}
	}
	if(dep[t] == -1) return 0;
	else return 1;
}

int dfs (int x, int mx) {
	int flow;
	if(x == t) return mx;
	for(int i = cur[x]; i != -1; i = edge[i].next) {
		cur[x] = i;
		int v = edge[i].to, cap = edge[i].cap;
		if(cap > 0 && dep[v] == dep[x] + 1 && (flow = dfs(v, min(mx, cap))) ) {
			edge[i].cap -= flow;
			edge[i^1].cap += flow;
			return flow;
		}
	}
	return 0;
}

void Dinic () {
	int res = 0;
	while(bfs()) 
		while(res = dfs(s, INF))
			maxflow += res;
}

int main () {
	memset(head, -1, sizeof(head));
	int u, v, low, up, sum = 0;
	scanf("%d %d", &n, &m);
	s = 0, t = n + 1;
	for(int i = 1; i <= m; i++) {
		scanf("%d %d %d %d", &u, &v, &low, &up);
		addEdge(u, v, up-low); //分离必要弧
		edge[cnt].up = up;
		addEdge(v, u, 0); //反向建边
		addEdge(u, t, low), addEdge(t, u, 0); //连接必要弧和s以及t的边
		addEdge(s, v, low), addEdge(v, s, 0);
		sum += low; //记录必要弧的权值之和
	}
	Dinic();
	if(maxflow == sum) {
		printf("YES\n");
		for(int i = 0; i <= cnt; i++) 
			if(edge[i].up) 
				printf("%d\n", edge[i].up - edge[i].cap); //求可行流
	}
	else 
		printf("NO\n");
	
	return 0;
}

有源汇的上下界最大流。

需要首先将有源汇的上下界最大流转换为无源汇的一个循环流,按照求解可行流的方法,建立超级源点汇点,然后使用最大流算法计算。

假设网络如下

建立一个无源汇的循环流很简单,只需从连一条 t &ThinSpace;&ThinSpace; − &gt; s t\,\,-&gt;s t>s的权值为 ∞ + \infty^+ +的弧即可

然后把之前的 s s s t t t当做普通的点处理,建立超级源点和超级汇点 s t st st,和 e d ed ed

然后使用最大流算法,求从 s t st st e d ed ed的流,并判断是否是一个可行流,如果是再用最大流算法跑一遍从 s s s t t t即可

  • 注意要建立很多条边,每一条边都要反向建立边

题目链接

#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
#define N 205
#define M 10005
#define INF 0x7fffffff
#define min(a, b) a>b?b:a
using namespace std;

struct Edge {
	int to, next, cap;
}edge[M * 10];

int n, m, s, t, maxflow, cnt = -1;
int head[N], dep[N], cur[N];
queue <int> q;
inline void addEdge (int u, int v, int cost) {
	edge[++cnt].cap = cost;
	edge[cnt].to = v;
	edge[cnt].next = head[u];
	head[u] = cnt;
}

bool bfs (int ss, int tt) {
	while(!q.empty()) q.pop();
	for(int i = 0; i <= n + 1; i++) cur[i] = head[i], dep[i] = -1;
	dep[ss] = 0;
	q.push(ss);
	while(!q.empty()) {
		int x = q.front(); q.pop();
		for(int i = head[x]; i != -1; i = edge[i].next) {
			int v = edge[i].to, cap = edge[i].cap;
			if(cap > 0 && dep[v] == -1) {
				dep[v] = dep[x] + 1;
				q.push(v);
			}
		}
	}
	if(dep[tt] == -1) return 0;
	else return 1;
}

int dfs (int x, int tt, int mx) {
	int flow;
	if(x == tt) return mx;
	for(int i = cur[x]; i != -1; i = edge[i].next) {
		cur[x] = i;
		int v = edge[i].to, cap = edge[i].cap;
		if(cap > 0  && dep[v] == dep[x] + 1 && (flow = dfs(v, tt, min(cap, mx))) ) {
			edge[i].cap -= flow;
			edge[i^1].cap += flow;
			return flow;
		}
	}
	return 0;
}

void Dinic(int ss, int tt) {
	int res = 0;
	while( bfs(ss, tt) ) 
		while( res = dfs(ss, tt, INF) )
			maxflow += res;
}
int main () {
	int u, v, cost, st, ed, low, up, sum = 0;
	scanf("%d %d %d %d", &n, &m, &s, &t);
	st = 0, ed = n + 1;
	memset(head, -1, sizeof(head));
	for(int i = 1; i <= m; i++) {
		scanf("%d %d %d %d", &u, &v, &low, &up);
		addEdge(u, v, up - low), addEdge(v, u, 0);
		addEdge(u, ed, low), addEdge(ed, u, 0);
		addEdge(st, v, low), addEdge(v, st, 0);
		sum += low;
	}
	addEdge(t, s, INF);
	addEdge(s, t, 0);
	Dinic(st, ed); //先判断可行流
	if(sum > maxflow) {
		printf("please go home to sleep\n");
		return 0;
	} 
	maxflow = 0;
	Dinic(s, t); //再判断最大流
	printf("%d\n", maxflow);
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值