有上下界网络流的意思即为每条边不仅有最大流量,还有最小流量。除了需要满足每条边流量在此范围内以外,还需满足每个点流量平衡(除了源点和汇点)可以分为以下几个类型。
引用 https://www.cnblogs.com/birchtree/p/12912607.html
无源汇可行流
即找到一种可行的流法。其实我们要做的其实就是转换为普通的最大流,我们强行给每条边跑最小流量,然后把最大流量减去最小流量作为新的流量上界(没有下界),但是这时可能不满足流量平衡,所以我们还要调整(利用残余网络的流量)。我们新建两个源点和汇点(ss和tt),如果一个点的流量是正的,那么从 (ss) 连一条到其的边,流量为这个点的流量,如果是负的就从其向 (tt) 流负的这个点的流量的边。然后跑最大流,如果 (ss,tt) 满流则说明存在可行流,为 (maxflow(ss,tt)+sum l_i)。
有源汇可行流
设原网络的源和汇分别为 (s,t) 我们在原网络上加一条边 ((t,s,0,infty)),相当于把到汇点的所有流量都流回源点,这样每个点流量都守恒。 然后套无源汇的方法即可。注意总流量=t到s的无穷边在原图中的流量
有源汇最大(最小)流
先按上面的方法求出一个有源汇有上下界可行流.然后在附加网络上再跑一次 (s) 到 (t) 的最大流(注意不是ss,tt!)。最大流=可行流+第二次跑的s到t最大流。
再跑一次最大流是因为附加网络上属于原图的边还有流量没被“榨干”。容易发现只要附加网络上不属于原图的边满流,属于原图的边怎么跑流量都是守恒的。因为第一次跑最大流已经保证所有点守恒,第二次跑最大流不会经过不属于原图的边,因此等价于对原图跑一次普通的最大流,除源汇外流量守恒。两次合起来总流量一定守恒,这就保证了正确性。
同理求最小流就跑一次 (t) 到 (s) 的最大流。最小流=可行流-第二次跑的t到s最大流。这是因为Dinic过程中反向边的流量增加等价于正向边的的流量减少。
有源汇最小费用流
找到一条可行的最小费用流(不要求流最大)。
按有源汇可行流的方法建图,把原图中的边带上费用(其他不带费用(0))。总费用=(mincost(ss,tt)+sum l_ic_i)
例题
首先不难看出这是一道网络流(从数据范围,题目描述中可以看出)。考虑怎么建图,由于又有上界又有下界所以考虑上下界有源汇最大流。
考虑对于每一天建立一个点,每个女孩建立一个点,再建立一个源点和汇点,考虑从每个女孩连一条 ([G_i,infty]) 边向汇点,从源点向每天连一条 ([0,D_i]) 的边,然后每天向女孩连 ([l_i,r_i]) 然后跑上下界最大流即可。
#include <bits/stdc++.h>
using namespace std;
const int MAXM = 1005;
const int MAXN = 370;
const int inf = 0x7f7f7f7f;
template <typename T>
void read(T &x) {
T flag = 1;
char ch = getchar()kao;
for (; !isdigit(ch); ch = getchar()) if (ch == '-') flag = -1;
for (x = 0; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
x *= flag;
}
struct node{
int pre, to, val;
}edge[1000005];
int head[1000005], flow[1000005], d[1000005], cur[1000005], tot;
int n, m;
int G[MAXM], num;
int gid[MAXM], did[MAXN], ans;
queue<int> q;
int S, T;
bool bfs(int s, int t) {
for (int i = 1; i <= num; i++) cur[i] = head[i], d[i] = 0;
d[s] = 1;
q.push(s);
while (!q.empty()) {
int x = q.front();
q.pop();
for (int i = head[x]; i; i = edge[i].pre) {
int y = edge[i].to;
if (edge[i].val > 0 && !d[y]) {
d[y] = d[x] + 1;
q.push(y);
}
}
}
return d[t];
}
int dinic(int x, int t, int now) {
if (x == t || now == 0) return now;
int rst = now;
for (int i = cur[x]; i && rst; i = edge[i].pre) {
int y = edge[i].to;
if (edge[i].val > 0 && d[y] == d[x] + 1) {
int k = dinic(y, t, min(rst, edge[i].val));
if (!k) d[y] = 0;
edge[i].val -= k;
edge[i ^ 1].val += k;
rst -= k;
}
cur[x] = i;
}
return now - rst;
}
void add_edge(int u, int v, int l) {
edge[++tot] = node{head[u], v, l};
head[u] = tot;
edge[++tot] = node{head[v], u, 0};
head[v] = tot;
}
void init() {
for (int i = 1; i <= num; i++) flow[i] = head[i] = 0;
tot = 1;
num = 0;
ans = 0;
S = ++num, T = ++num;
}
int maxflow(int s, int t) {
int ret = 0, now;
while (bfs(s, t)) {
while ((now = dinic(s, t, inf)) > 0) {
ret += now;
}
}
return ret;
}
int main() {
while (scanf("%d%d", &n, &m) != EOF) {
init();
for (int i = 1; i <= m; i++) {
read(G[i]);
gid[i] = ++num;
add_edge(gid[i], T, inf - G[i]);
flow[gid[i]] -= G[i];
flow[T] += G[i];
}
for (int i = 1; i <= n; i++) {
int c, D;
read(c); read(D);
did[i] = ++num;
add_edge(S, did[i], D);
for (int j = 1; j <= c; j++) {
int t, l, r;
read(t); read(l); read(r);
t++;
add_edge(did[i], gid[t], r - l);
flow[did[i]] -= l;
flow[gid[t]] += l;
}
}
add_edge(T, S, inf);
int e = tot ^ 1;
int ss = ++num, tt = ++num;
for (int i = 1; i <= num - 2; i++) {
if (flow[i] > 0) {
add_edge(ss, i, flow[i]);
} else if (flow[i] < 0) {
add_edge(i, tt, -flow[i]);
}
}
bool flag = true;
maxflow(ss, tt);
ans = inf - edge[e].val;
edge[e].val = edge[e ^ 1].val = 0;
for (int i = head[ss]; i; i = edge[i].pre) {
if (edge[i].val > 0) {
flag = false;
}
}
if (flag) {
printf("%d
", ans + maxflow(S, T));
} else {
puts("-1");
printf("
");
}
}
return 0;
}