【Luogu P3980】[NOI2008] 志願者招募
阿新 • • 發佈:2021-07-18
志願者招募
連結:
題目大意:
第 \(i\) 天要 \(a_i\) 個人。第 \(j\) 種人從第 \(s_j\) 天干到第 \(t_j\) 天,要花 \(c_j\) 元。
找到一種方案使得付出的錢最少。
正文:
這是一種線性規劃類問題,最小化 \(\sum_{i=1}^m x_ic_i\),且滿足:
\[\left\{\begin{matrix} \sum_{i=1}^m [s_i\leq1\leq t_i]x_i &\geq a_1 \\ \sum_{i=1}^m [s_i\leq2\leq t_i]x_i &\geq a_2 \\ \vdots \end{matrix}\right.\]考慮用費用流。在用費用流解決某線性規劃類的問題時,可以考慮把約束條件的每行看作一點(本題為第 \(i\)
考慮建模:
- \(S\) 連向 \(1\)、\(n+1\) 連向 \(T\),流量是 \(+\infty\),費用 \(0\)。
- \(i\) 連向 \(i+1\),流量是 \(+\infty-a_i\),費用 \(0\)。
- \(s_i\) 連向 \(t_i+1\),流量 \(+\infty\),費用 \(c_i\)。
那麼最大流一定是 \(+\infty\),否則無解。
程式碼:
const int N = 1010, M = 10010; const ll inf = 1e18; inline ll Read() { ll x = 0, f = 1; char c = getchar(); while (c != '-' && (c < '0' || c > '9')) c = getchar(); if (c == '-') f = -f, c = getchar(); while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar(); return x * f; } int n, m, s, t; struct edge { ll to, w, val, nxt; }e[(N + M) << 1]; int head[N], tot = 1; void add(int u, int v, ll w, ll val) { e[++tot] = (edge) {v, w, val, head[u]}, head[u] = tot; e[++tot] = (edge) {u, 0, -val, head[v]}, head[v] = tot; } int pre[N]; bool vis[N]; ll dis[N], incf[N]; queue <int> q; bool SPFA() { memset (dis, 0x3f3f3f3f, sizeof dis); while (!q.empty()) q.pop(); q.push(s); vis[s] = 1; dis[s] = 0; incf[s] = inf; while (!q.empty()) { int u = q.front(); q.pop(); vis[u] = 0; for (int i = head[u]; i; i = e[i].nxt) { int v = e[i].to; if (dis[v] > dis[u] + e[i].val && e[i].w) { dis[v] = dis[u] + e[i].val; pre[v] = i, incf[v] = min(incf[u], e[i].w); if (!vis[v]) vis[v] = 1, q.push(v); } } } return dis[t] < inf; } ll MCMF() { ll cost = 0; while (SPFA()) { int u = t; cost += dis[t] * incf[t]; for (; u != s; u = e[pre[u] ^ 1].to) e[pre[u]].w -= incf[t], e[pre[u] ^ 1].w += incf[t]; } return cost; } int main() { n = Read(), m = Read(), s = n + 2, t = s + 1; add(s, 1, inf, 0); for (int i = 1; i <= n; i++) { ll val = Read(); add(i, i + 1, inf - val, 0); } for (int i = 1; i <= m; i++) { int u = Read(), v = Read(); ll w = Read(); add(u, v + 1, inf, w); } add(n + 1, t, inf, 0); printf ("%lld\n", MCMF()); return 0; }