无源无汇有容量上下界网络的可行流
网络不仅无源无汇,而且每条边除了有容量上界c之外,还有一个容量下界b,所有结点都应满足“入流=出流”这个流量平衡条件,要求出满足这些流量平衡条件的可行流。
这时候就有一个结论:如果可行流存在,则原图一定是强连通的。因此可以先检查原图如果不是强连通的,则必然不存在可行流。
附加源点s和汇点t,把每条边
u
→
v
u\rightarrow v
u→v拆成3条,再进行合并,如下图所示。求出最大流
f
l
o
w
flow
flow后,若
f
l
o
w
=
∑
b
i
flow=\sum b_i
flow=∑bi则可行流存在。如果要输出每条边的实际流量只用将新图边
u
→
v
u\rightarrow v
u→v的的流量加上下界b即可。
模板题 zoj-2314 Reactor Cooling
#include <iostream>
#include <cstring>
using namespace std;
#define INF 0x7f7f7f7f
#define N 202
struct edge {int u, v, cap, flow;} e[N*N];
int g[N][N], q[N], p[N], d[N], cur[N], num[N], cnt[N], cs[N], ct[N], h[N*N>>1], b[N*N>>1], c, m, n, f; bool vis[N];
void add_edge(int u, int v, int cap) {
e[c].u = u; e[c].v = v; e[c].cap = cap; e[c].flow = 0; g[u][cnt[u]++] = c++;
e[c].u = v; e[c].v = u; e[c].cap = 0; e[c].flow = 0; g[v][cnt[v]++] = c++;
}
bool bfs(int s, int t) {
memset(vis, 0, sizeof(vis)); memset(d, 0, sizeof(d)); q[0] = t; d[t] = 0; vis[t] = true;
int head = 0, tail = 1;
while (head < tail) {
int v = q[head++];
for (int i=0; i<cnt[v]; ++i) {
const edge& ee = e[g[v][i]^1];
if (!vis[ee.u] && ee.cap > ee.flow) vis[ee.u] = true, d[ee.u] = d[v] + 1, q[tail++] = ee.u;
}
}
return vis[s];
}
int max_flow(int s, int t) {
int flow = 0, u = s;
if (!bfs(s, t)) return 0;
memset(num, 0, sizeof(num)); memset(cur, 0, sizeof(cur));
for (int i=0; i<=t; ++i) ++num[d[i]];
while (d[s] <= t) {
if (u == t) {
int a = INF;
for (int v=t; v!=s; v = e[p[v]].u) a = min(a, e[p[v]].cap - e[p[v]].flow);
for (int v=t; v!=s; v = e[p[v]].u) e[p[v]].flow += a, e[p[v]^1].flow -= a;
flow += a; u = s;
}
int ok = 0;
for (int i=cur[u]; i<cnt[u]; ++i) {
const edge& ee = e[g[u][i]];
if (ee.cap > ee.flow && d[u] == d[ee.v] + 1) {
ok = 1; p[ee.v] = g[u][i]; cur[u] = i; u = ee.v;
break;
}
}
if (!ok) {
int m = t;
for (int i=0; i<cnt[u]; ++i) {
const edge& ee = e[g[u][i]];
if (ee.cap > ee.flow) m = min(m, d[ee.v]);
}
if (--num[d[u]] == 0) break;
++num[d[u] = m + 1]; cur[u] = 0;
if (u != s) u = e[p[u]].u;
}
}
return flow;
}
void solve() {
cin >> n >> m; memset(cnt, c = f = 0, sizeof(cnt)); memset(cs, 0, sizeof(cs)); memset(ct, 0, sizeof(ct));
int s = 0, t = n+1;
for (int i=0; i<m; ++i) {
int u, v, s; cin >> u >> v >> b[i] >> s;
f += b[i]; cs[v] += b[i]; ct[u] += b[i]; h[i] = c; add_edge(u, v, s-b[i]);
}
for (int i=1; i<=n; ++i) {
if (cs[i]) add_edge(s, i, cs[i]);
if (ct[i]) add_edge(i, t, ct[i]);
}
if (max_flow(s, t) == f) {
cout << "YES" << endl;
for (int i=0; i<m; ++i) cout << e[h[i]].flow + b[i] << endl;
} else cout << "NO" << endl;
}
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int t; cin >> t;
while (t--) {
solve();
if (t) cout << endl;
}
return 0;
}
需要稍加分析的题目 HDU-4940 Destroy Transportation system
题意:给出一个有向强连通图,每条边有两个值分别是删除该边的代价D和把该边建成无向边的代价B(建立无向边的前提是删除该边)问是否存在一个顶点的非空真子集S和其补集T,破坏所有S集合到T集合的边代价和是X,然后边建T到S的边为无向边代价和是Y,满足Y<X;满足输出unhappy,否则输出happy。
分析:原来每条边转化成下界为D上界为D+B,判断是否存在可行流即可。如果存在可行流,那么说明对于任意的S集合流出的肯定等于流入的,流出的计算的X肯定小于等于这个流量(X是下界之和), 计算出来的Y (上界之和)肯定大于等于这个流量,肯定满足X<=Y。
有源有汇有容量上下界网络的可行流
这时候往往汇点和源点无边相连(原图不是强连通的),需要连一条汇点到源点且容量上下界为
[
0
,
i
n
f
]
[0,inf]
[0,inf]的边,就变成了无源无汇有容量上下界网络的可行流。
例题:poj2396 Budget
本题首先可以用和UVa11082 Matrix Decompressing一样的办法求,只需要先将每个矩阵元素减掉下限(同时行列和也减)之后就和UVa11082一样求最大流即可,最后输出答案时需要加回减掉的下限。
也可以用有源有汇有容量上下界网络的可行流来处理,建图方法首先和UVa11082一样,然后连一条汇点到源点且容量上下界均为矩阵所有元素之和的边。
#include <iostream>
#include <cstring>
using namespace std;
#define INF 0x7f7f7f7f
#define M 202
#define N 22
#define T 224
struct edge {int u, v, cap, flow;} e[M*N<<1];
int g[T][M], l[M][N], u[M][N], q[T], p[T], d[T], cur[T], num[T], cnt[T], cs[T], ct[T], c, m, n; bool vis[T];
void add_edge(int u, int v, int cap) {
e[c].u = u; e[c].v = v; e[c].cap = cap; e[c].flow = 0; g[u][cnt[u]++] = c++;
e[c].u = v; e[c].v = u; e[c].cap = 0; e[c].flow = 0; g[v][cnt[v]++] = c++;
}
bool bfs(int s, int t) {
memset(vis, 0, sizeof(vis)); memset(d, 0, sizeof(d)); q[0] = t; d[t] = 0; vis[t] = true;
int head = 0, tail = 1;
while (head < tail) {
int v = q[head++];
for (int i=0; i<cnt[v]; ++i) {
const edge& ee = e[g[v][i]^1];
if (!vis[ee.u] && ee.cap > ee.flow) vis[ee.u] = true, d[ee.u] = d[v] + 1, q[tail++] = ee.u;
}
}
return vis[s];
}
bool upd(char s, int v, int r, int c) {
if (s == '=') {
if (l[r][c] > v || u[r][c] < v) return false;
l[r][c] = u[r][c] = v;
} else if (s == '>') {
if (u[r][c] < ++v) return false;
l[r][c] = max(l[r][c], v);
} else {
if (l[r][c] > --v) return false;
u[r][c] = min(u[r][c], v);
}
return true;
}
bool check_lu() {
int k; bool ok = true; cin >> k;
while (k--) {
int r, c, v; char s; cin >> r >> c >> s >> v;
if (r) {
if (c) {
if (!upd(s, v, r, c)) ok = false;
} else for (c=1; c<=n; ++c) if (!upd(s, v, r, c)) {
ok = false; break;
}
} else if (c) {
for (r=1; r<=m; ++r) if (!upd(s, v, r, c)) {
ok = false; break;
}
} else for (r=1; r<=m; ++r) for (c=1; c<=n; ++c) if (!upd(s, v, r, c)) {
ok = false; break;
}
}
return ok;
}
void solve() {
cin >> m >> n; memset(l, 0, sizeof(l)); memset(u, 127, sizeof(u)); memset(cnt, c = 0, sizeof(cnt));
int s = 0, t = m+n+1, f = 0, f2 = 0; memset(cs, 0, sizeof(cs)); memset(ct, 0, sizeof(ct));
for (int i=1, x; i<=m; ++i) cin >> x, f += x, add_edge(s, i, x);
for (int i=1, x; i<=n; ++i) cin >> x, f2 += x, add_edge(m+i, t, x);
if (!check_lu() || f != f2) {
cout << "IMPOSSIBLE" << endl;
return;
}
add_edge(t+1, s, f); add_edge(t, t+2, f); s = t+1; t = s+1;
for (int r=1; r<=m; ++r) for (int c=1; c<=n; ++c) {
f += l[r][c]; cs[m+c] += l[r][c]; ct[r] += l[r][c];
if (u[r][c] > l[r][c]) add_edge(r, m+c, u[r][c] - l[r][c]);
}
for (int r=1; r<=m; ++r) if (ct[r]) add_edge(r, t, ct[r]);
for (int c=1; c<=n; ++c) if (cs[m+c]) add_edge(s, m+c, cs[m+c]);
if (!bfs(s, t)) {
if (f) cout << "IMPOSSIBLE" << endl;
else for (int r=1; r<=m; ++r) for (int c=1; c<=n; ++c) cout << l[r][c], cout << (c<n ? ' ' : '\n');
return;
}
memset(num, 0, sizeof(num)); memset(cur, 0, sizeof(cur));
for (int i=0; i<=t; ++i) ++num[d[i]];
for (int u = s; d[s] <= t;) {
if (u == t) {
int a = INF;
for (int v=t; v!=s; v = e[p[v]].u) a = min(a, e[p[v]].cap - e[p[v]].flow);
for (int v=t; v!=s; v = e[p[v]].u) e[p[v]].flow += a, e[p[v]^1].flow -= a;
f -= a; u = s;
}
bool ok = false;
for (int i=cur[u]; i<cnt[u]; ++i) {
const edge& ee = e[g[u][i]];
if (ee.cap > ee.flow && d[u] == d[ee.v] + 1) {
ok = true; p[ee.v] = g[u][i]; cur[u] = i; u = ee.v;
break;
}
}
if (!ok) {
int m = t;
for (int i=0; i<cnt[u]; ++i) {
const edge& ee = e[g[u][i]];
if (ee.cap > ee.flow) m = min(m, d[ee.v]);
}
if (--num[d[u]] == 0) break;
++num[d[u] = m + 1]; cur[u] = 0;
if (u != s) u = e[p[u]].u;
}
}
if (!f) {
for (int i=1; i<=m; ++i) for (int j=0; j<cnt[i]; ++j) {
const edge& ee = e[g[i][j]];
if (ee.v > m && ee.v <= m+n) l[i][ee.v-m] += ee.flow;
}
for (int r=1; r<=m; ++r) for (int c=1; c<=n; ++c) cout << l[r][c], cout << (c<n ? ' ' : '\n');
} else cout << "IMPOSSIBLE" << endl;
}
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int t; cin >> t;
while (t--) {
solve();
if (t) cout << endl;
}
return 0;
}
有源有汇有容量上下界网络的最大流
先求有源有汇有容量上下界网络的可行流(可行流不存在则无解),再在残量网络上求一次源点到汇点的最大流即可。
模板题 zoj-3229 Shoot the Bullet
题意:多组数据,读到文件结束。对于每一组数据,第一行为正整数
n
,
m
n,m
n,m表示
n
n
n天,
m
m
m个少女。接下来一行,
m
m
m个正整数
G
1
,
G
2
.
.
.
G
m
G_1,G_2...G_m
G1,G2...Gm分别表示每个少女总共至少要拍的照片张数。接下来
n
n
n组,每一组第一行有两个整数
C
i
,
D
i
C_i,D_i
Ci,Di,表示这一天有
C
i
C_i
Ci个少女要拍照,这一天的照片总数不超过
D
i
D_i
Di。接着有
C
i
C_i
Ci行,每行三个整数
k
,
L
k
,
R
k
k,L_k,R_k
k,Lk,Rk,表示在这一天里
k
k
k号少女要拍的照片数目处于
[
L
k
,
R
k
]
[L_k,R_k]
[Lk,Rk]区间。(注意少女是从0开始编号的。)满足这些条件以后,所有天的照片总数越多越好。如果有解输出照片总数,并按顺序输出每一天的少女拍照数目,否则输出-1,每组答案后输出一空行。
#include <iostream>
#include <cstring>
using namespace std;
#define INF 0x7f7f7f7f
#define N 1369
struct edge {int u, v, cap, flow;} e[60*N];
int g[N][N], b[365][N], q[N], p[N], d[N], cur[N], num[N], cnt[N], cs[N], ct[N], c, m, n, f; bool vis[N];
void add_edge(int u, int v, int cap) {
e[c].u = u; e[c].v = v; e[c].cap = cap; e[c].flow = 0; g[u][cnt[u]++] = c++;
e[c].u = v; e[c].v = u; e[c].cap = 0; e[c].flow = 0; g[v][cnt[v]++] = c++;
}
bool bfs(int s, int t) {
memset(vis, 0, sizeof(vis)); memset(d, 0, sizeof(d)); q[0] = t; d[t] = 0; vis[t] = true;
int head = 0, tail = 1;
while (head < tail) {
int v = q[head++];
for (int i=0; i<cnt[v]; ++i) {
const edge& ee = e[g[v][i]^1];
if (!vis[ee.u] && ee.cap > ee.flow) vis[ee.u] = true, d[ee.u] = d[v] + 1, q[tail++] = ee.u;
}
}
return vis[s];
}
int max_flow(int s, int t, int n) {
int flow = 0, u = s;
if (!bfs(s, t)) return 0;
memset(num, 0, sizeof(num)); memset(cur, 0, sizeof(cur));
for (int i=0; i<n; ++i) ++num[d[i]];
while (d[s] < n) {
if (u == t) {
int a = INF;
for (int v=t; v!=s; v = e[p[v]].u) a = min(a, e[p[v]].cap - e[p[v]].flow);
for (int v=t; v!=s; v = e[p[v]].u) e[p[v]].flow += a, e[p[v]^1].flow -= a;
flow += a; u = s;
}
int ok = 0;
for (int i=cur[u]; i<cnt[u]; ++i) {
const edge& ee = e[g[u][i]];
if (ee.cap > ee.flow && d[u] == d[ee.v] + 1) {
ok = 1; p[ee.v] = g[u][i]; cur[u] = i; u = ee.v;
break;
}
}
if (!ok) {
int m = n-1;
for (int i=0; i<cnt[u]; ++i) {
const edge& ee = e[g[u][i]];
if (ee.cap > ee.flow) m = min(m, d[ee.v]);
}
if (--num[d[u]] == 0) break;
++num[d[u] = m + 1]; cur[u] = 0;
if (u != s) u = e[p[u]].u;
}
}
return flow;
}
void solve() {
memset(cnt, c = f = 0, sizeof(cnt)); memset(cs, 0, sizeof(cs)); memset(ct, 0, sizeof(ct));
int s = m+n, t = s+1;
for (int i=0, x; i<m; ++i) cin >> x, f += x, cs[i] += x, ct[s] += x, add_edge(s, i, INF);
for (int i=0; i<n; ++i) {
int c, d; cin >> c >> d; add_edge(m+i, t, d);
while (c--) {
int k, l, r; cin >> k >> l >> r; f += l; cs[m+i] += l; ct[k] += l; b[i][k] = l; add_edge(k, m+i, r-l);
}
}
add_edge(t, s, INF); s = t+1; t = s+1;
for (int i=s-2; i>=0; --i) {
if (cs[i]) add_edge(s, i, cs[i]);
if (ct[i]) add_edge(i, t, ct[i]);
}
if (max_flow(s, t, t+1) == f) {
cout << max_flow(s-2, t-2, t+1) << endl;
for (int i=0; i<n; ++i) for (int j=0, k=cnt[m+i]; j<k; ++j) {
const edge &ee = e[g[m+i][j]^1];
if (ee.u < m) cout << b[i][ee.u] + ee.flow << endl;
}
} else cout << -1 << endl;
cout << endl;
}
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
while (cin >> n >> m) solve();
return 0;
}
有源有汇有容量上下界网络的最小流
先求有源有汇有容量上下界网络的可行流(可行流不存在则无解),此时得到汇点连向源点的无限边的流量
f
f
f,删除此边(及其反向边)后在残量网络上求一次汇点到源点的最大流
f
l
o
w
flow
flow,
f
−
f
l
o
w
f-flow
f−flow即是答案。
模板题 HDU-3157 Crazy Circuits
#include <iostream>
#include <cstring>
using namespace std;
#define INF 0x7f7f7f7f
#define N 54
struct edge {int u, v, cap, flow;} e[24*N];
int g[N][N], q[N], p[N], d[N], cur[N], num[N], cnt[N], cs[N], ct[N], c, m, n, f; bool vis[N];
void add_edge(int u, int v, int cap) {
e[c].u = u; e[c].v = v; e[c].cap = cap; e[c].flow = 0; g[u][cnt[u]++] = c++;
e[c].u = v; e[c].v = u; e[c].cap = 0; e[c].flow = 0; g[v][cnt[v]++] = c++;
}
bool bfs(int s, int t) {
memset(vis, 0, sizeof(vis)); memset(d, 0, sizeof(d)); q[0] = t; d[t] = 0; vis[t] = true;
int head = 0, tail = 1;
while (head < tail) {
int v = q[head++];
for (int i=0; i<cnt[v]; ++i) {
const edge& ee = e[g[v][i]^1];
if (!vis[ee.u] && ee.cap > ee.flow) vis[ee.u] = true, d[ee.u] = d[v] + 1, q[tail++] = ee.u;
}
}
return vis[s];
}
int max_flow(int s, int t, int n) {
int flow = 0, u = s;
if (!bfs(s, t)) return 0;
memset(num, 0, sizeof(num)); memset(cur, 0, sizeof(cur));
for (int i=0; i<n; ++i) ++num[d[i]];
while (d[s] < n) {
if (u == t) {
int a = INF;
for (int v=t; v!=s; v = e[p[v]].u) a = min(a, e[p[v]].cap - e[p[v]].flow);
for (int v=t; v!=s; v = e[p[v]].u) e[p[v]].flow += a, e[p[v]^1].flow -= a;
flow += a; u = s;
}
int ok = 0;
for (int i=cur[u]; i<cnt[u]; ++i) {
const edge& ee = e[g[u][i]];
if (ee.cap > ee.flow && d[u] == d[ee.v] + 1) {
ok = 1; p[ee.v] = g[u][i]; cur[u] = i; u = ee.v;
break;
}
}
if (!ok) {
int m = n-1;
for (int i=0; i<cnt[u]; ++i) {
const edge& ee = e[g[u][i]];
if (ee.cap > ee.flow) m = min(m, d[ee.v]);
}
if (--num[d[u]] == 0) break;
++num[d[u] = m + 1]; cur[u] = 0;
if (u != s) u = e[p[u]].u;
}
}
return flow;
}
int read() {
int x; char c; cin >> c;
if (c == '+') return 0;
if (c == '-') return n+1;
cin.unget(); cin >> x;
return x;
}
void solve() {
memset(cnt, c = f = 0, sizeof(cnt)); memset(cs, 0, sizeof(cs)); memset(ct, 0, sizeof(ct));
int s = 0, t = n+1;
while (m--) {
int u = read(), v = read(), i; cin >> i; add_edge(u, v, INF); cs[v] += i; ct[u] += i; f += i;
}
add_edge(t, s, INF); s = t+1; t = s+1;
for (int i=s-1; i>=0; --i) {
if (cs[i]) add_edge(s, i, cs[i]);
if (ct[i]) add_edge(i, t, ct[i]);
}
if (max_flow(s, t, t+1) == f) {
cout << INF - max_flow(s-1, 0, t+1) << endl;
} else cout << "impossible" << endl;
}
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
while (cin >> n >> m && m) solve();
return 0;
}
模板题 LibreOJ-117
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
#define INF 0x7f7f7f7f
#define M 750024
#define N 50006
struct edge {int u, v, cap, flow;} e[M]; vector<int> g[N];
int q[N], p[N], d[N], cur[N], num[N], cs[N], ct[N], c, m, n, f; bool vis[N];
void add_edge(int u, int v, int cap) {
e[c].u = u; e[c].v = v; e[c].cap = cap; e[c].flow = 0; g[u].push_back(c++);
e[c].u = v; e[c].v = u; e[c].cap = 0; e[c].flow = 0; g[v].push_back(c++);
}
bool bfs(int s, int t) {
memset(vis, 0, sizeof(vis)); memset(d, 0, sizeof(d)); q[0] = t; d[t] = 0; vis[t] = true;
int head = 0, tail = 1;
while (head < tail) {
int v = q[head++];
for (int i=0; i<g[v].size(); ++i) {
const edge& ee = e[g[v][i]^1];
if (!vis[ee.u] && ee.cap > ee.flow) vis[ee.u] = true, d[ee.u] = d[v] + 1, q[tail++] = ee.u;
}
}
return vis[s];
}
int max_flow(int s, int t, int n) {
int flow = 0, u = s;
if (!bfs(s, t)) return 0;
memset(num, 0, sizeof(num)); memset(cur, 0, sizeof(cur));
for (int i=0; i<n; ++i) ++num[d[i]];
while (d[s] < n) {
if (u == t) {
int a = INF;
for (int v=t; v!=s; v = e[p[v]].u) a = min(a, e[p[v]].cap - e[p[v]].flow);
for (int v=t; v!=s; v = e[p[v]].u) e[p[v]].flow += a, e[p[v]^1].flow -= a;
flow += a; u = s;
}
int ok = 0;
for (int i=cur[u]; i<g[u].size(); ++i) {
const edge& ee = e[g[u][i]];
if (ee.cap > ee.flow && d[u] == d[ee.v] + 1) {
ok = 1; p[ee.v] = g[u][i]; cur[u] = i; u = ee.v;
break;
}
}
if (!ok) {
int m = n-1;
for (int i=0; i<g[u].size(); ++i) {
const edge& ee = e[g[u][i]];
if (ee.cap > ee.flow) m = min(m, d[ee.v]);
}
if (--num[d[u]] == 0) break;
++num[d[u] = m + 1]; cur[u] = 0;
if (u != s) u = e[p[u]].u;
}
}
return flow;
}
void solve() {
memset(cs, c = f = 0, sizeof(cs)); memset(ct, 0, sizeof(ct));
int s, t, x; cin >> s >> t;
for (int i=n+1; i>=0; --i) g[i].clear();
while (m--) {
int u, v, l, r; cin >> u >> v >> l >> r; cs[v] += l; ct[u] += l; f += l;
if (l < r) add_edge(u, v, r-l);
}
x = c; add_edge(t, s, INF);
for (int i=1; i<=n; ++i) {
if (cs[i]) add_edge(0, i, cs[i]);
if (ct[i]) add_edge(i, n+1, ct[i]);
}
if (max_flow(0, n+1, n+2) == f) {
int ans = e[x].flow; e[x].cap = e[x].flow = e[x^1].flow = 0;
cout << ans - max_flow(t, s, n+2) << endl;
} else cout << "please go home to sleep" << endl;
}
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
while (cin >> n >> m) solve();
return 0;
}
UVa1440/LA4597 Inspection
容量有下界的网络流求最小流,并且需要输出方案。