阿雅要在 n 天(n ≤ 365)內拍照 m 位少女(m ≤ 1000)。每位少女 i 有總照片數下限 Gᵢ。第 k 天有 Cₖ 個可拍目標,每日目標 j 有:少女編號 T、當日照片數範圍 [L, R]。每天總照片數 ≤ Dₖ。每位少女總照片數必須 ≥ Gᵢ。求最大總照片數;無可行解輸出 -1。若有解,輸出每日目標的實際拍照數(按輸入順序)。
標準網絡流模型:
目標:在滿足所有下界約束前提下,最大化 S→T 流量。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 2222;
const int maxm = 1000000;
const int INF = 0x3f3f3f3f;
struct Edge {
int to, next, cap, flow;
} edge[maxm];
int SZ, Adj[maxn];
int gap[maxn], dep[maxn], pre[maxn], cur[maxn];
void init() {
SZ = 0;
memset(Adj, -1, sizeof(Adj));
}
void addEdge(int u, int v, int w, int rw = 0) {
edge[SZ] = {v, Adj[u], w, 0}; Adj[u] = SZ++;
edge[SZ] = {u, Adj[v], rw, 0}; Adj[v] = SZ++;
}
int sap(int s, int t, int N) {
memset(gap, 0, sizeof(gap));
memset(dep, 0, sizeof(dep));
memcpy(cur, Adj, sizeof(Adj));
int u = s; pre[u] = -1; gap[0] = N; int ans = 0;
while (dep[s] < N) {
if (u == t) {
int mn = INF;
for (int i = pre[u]; ~i; i = pre[edge[i^1].to])
mn = min(mn, edge[i].cap - edge[i].flow);
for (int i = pre[u]; ~i; i = pre[edge[i^1].to]) {
edge[i].flow += mn;
edge[i^1].flow -= mn;
}
u = s; ans += mn; continue;
}
bool flag = false; int v;
for (int i = cur[u]; ~i; i = edge[i].next) {
v = edge[i].to;
if (edge[i].cap - edge[i].flow && dep[v] + 1 == dep[u]) {
flag = true; cur[u] = pre[v] = i; break;
}
}
if (flag) { u = v; continue; }
int mn = N;
for (int i = Adj[u]; ~i; i = edge[i].next)
if (edge[i].cap - edge[i].flow && dep[edge[i].to] < mn)
mn = dep[edge[i].to], cur[u] = i;
if (--gap[dep[u]] == 0) return ans;
dep[u] = mn + 1;
++gap[dep[u]];
if (u != s) u = edge[pre[u]^1].to;
}
return ans;
}
int G[maxn], demand[maxn];
int edgeId[maxm], lowBound[maxm], ecnt;
int main() {
int n, m, cas = 1;
while (scanf("%d%d", &n, &m) != EOF) {
init(); ecnt = 0;
memset(demand, 0, sizeof(demand));
for (int i = 1; i <= m; ++i) {
scanf("%d", &G[i]);
addEdge(n + i, n + m + 1, INF - G[i]);
demand[n + i] -= G[i];
demand[n + m + 1] += G[i];
}
for (int day = 1; day <= n; ++day) {
int C, D; scanf("%d%d", &C, &D);
addEdge(0, day, D); // S=0
for (int j = 0; j < C; ++j) {
int T, L, R; scanf("%d%d%d", &T, &L, &R);
++T; // 少女節點編號 n+1 ~ n+m
lowBound[ecnt] = L;
edgeId[ecnt++] = SZ;
addEdge(day, n + T, R - L);
demand[day] -= L;
demand[n + T] += L;
}
}
addEdge(n + m + 1, 0, INF); // T -> S
int SS = n + m + 2, TT = n + m + 3;
int sumDemand = 0;
for (int i = 0; i <= n + m + 1; ++i) {
if (demand[i] < 0) addEdge(i, TT, -demand[i]);
if (demand[i] > 0) { sumDemand += demand[i]; addEdge(SS, i, demand[i]); }
}
int flow1 = sap(SS, TT, n + m + 4);
if (flow1 != sumDemand) {
puts("-1");
} else {
// 移除 SS/TT:將其 Adj 設為 -1,但保留邊與殘量
Adj[SS] = Adj[TT] = -1;
int flow2 = sap(0, n + m + 1, n + m + 4);
printf("%d\n", flow2);
for (int i = 0; i < ecnt; ++i) {
int eid = edgeId[i];
printf("%d\n", edge[eid].flow + lowBound[i]);
}
}
putchar('\n');
}
return 0;
}
輸入:
2 3
12 12 12
3 18
0 3 9
1 3 9
2 3 9
3 18
0 3 9
1 3 9
2 3 9
...(更多測資見原題)
輸出:
36
6 6 6 6 6 6
36
9 6 3 3 6 9
-1
與 Sample Output 一致。
ZOJ 3229 是有源匯上下界最大流的經典模板題。核心在於:
此模板可直接遷移至所有「上下界約束 + 最大化目標」的網絡流問題。