题意:有 \(n\) 棵待种的植物,关系呈一张 DAG,其中边 \((u, v)\) 表示必须等植物 \(u\) 成熟之后才能种下植物 \(v\),第 \(i\) 棵植物种下后需要花费 \(t_i\) 时间成熟。你有 \(m\) 点魔法,可以使用 \(d_i\) 点魔法令 \(t_i\) 减一,可以多次对一棵植物使用魔法,求最终种完所有植物的最早时间。
\(n, |E|\le 50,\ m,d_i\le 10^9,\ t_i \le 25\)
添加一个源植物 \(s\) 和一个汇植物 \(t\),前者需要种下后才能种其他植物,后者必须在所有植物种完后才能种下。设 \(x_i, y_i\) 表示植物 \(i\) 的种下时刻和成熟时刻,设 \(p_i\) 表示植物 \(i\) 的施法次数。二分答案 \(mid\),那么应该满足:
-
\(y_s + mid \ge x_t\)
-
\(x_i \le y_i\)
-
\(x_i + t_i - d_i \le y_i\)
-
\(x_v \ge y_u , \ \forall (u, v) \in E\)
不难发现这是最小费用循环流的对偶形式(详见此处):
-
\(0 - x_t + y_s \ge -mid\)
-
\(0 - x_i + y_i \ge 0\)
-
\(d_i - x_i + y_i \ge t_i\)
-
\(0 - y_u + x_v \ge 0, \ \forall (u, v) \in E\)
但是某些 \(d_{u, v}\) 必须取 \(0\),考虑答案式子 \(\sum_{(u, v) \in E} w_{u, v}d_{u, v}\) 中,令 \(w_{u, v} = + \infty\) 即可。
点击查看代码
#include <bits/stdc++.h>namespace Initial {#define ll long long#define ull unsigned long long#define fi first#define se second#define mkp make_pair#define pir pair <ll, ll>#define pb push_back#define i128 __int128using namespace std;const ll maxn = 1050, inf = 1e13, mod = 1e9 + 7, L = 1e7 + 10;ll power(ll a, ll b = mod - 2, ll p = mod) {ll s = 1;while(b) {if(b & 1) s = 1ll * s * a %p;a = 1ll * a * a %p, b >>= 1;} return s;}template <class T>const inline ll pls(const T x, const T y) { return x + y >= mod? x + y - mod : x + y; }template <class T>const inline void add(T &x, const T y) { x = x + y >= mod? x + y - mod : x + y; }template <class T>const inline void chkmax(T &x, const T y) { x = x < y? y : x; }template <class T>const inline void chkmin(T &x, const T y) { x = x > y? y : x; }
} using namespace Initial;namespace Read {char buf[1 << 22], *p1, *p2;// #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, (1 << 22) - 10, stdin), p1 == p2)? EOF : *p1++)template <class T>const inline void rd(T &x) {char ch; bool neg = 0;while(!isdigit(ch = getchar()))if(ch == '-') neg = 1;x = ch - '0';while(isdigit(ch = getchar()))x = (x << 1) + (x << 3) + ch - '0';if(neg) x = -x;}
} using Read::rd;ll n, t[maxn], c[maxn], a[maxn][maxn], m, deg[maxn], sum;
char str[maxn][maxn];struct Graph {ll n, s, t, head[maxn], tot, cur[maxn];struct edge {ll v, w, c, nxt;} e[maxn];void ins(ll u, ll v, ll w, ll c) {e[++tot] = (edge) {v, w, c, head[u]}, head[u] = tot;e[++tot] = (edge) {u, 0, -c, head[v]}, head[v] = tot;}bool vis[maxn]; ll dis[maxn]; queue <ll> q;bool spfa() {for(ll i = 1; i <= n; i++) dis[i] = inf;dis[s] = 0, q.push(s);while(!q.empty()) {ll u = q.front(); q.pop(), vis[u] = false;for(ll i = head[u]; i; i = e[i].nxt) {ll v = e[i].v, w = e[i].w, c = e[i].c;if(w && dis[v] > dis[u] + c) {dis[v] = dis[u] + c;if(!vis[v])vis[v] = true, q.push(v);}}} return dis[t] < inf;}ll dfs(ll u, ll flow) {if(u == t) return flow;ll used = 0; vis[u] = true;for(ll i = cur[u]; i; cur[u] = i = e[i].nxt) {ll v = e[i].v, w = e[i].w, c = e[i].c;if(!vis[v] && w && dis[v] == dis[u] + c) {ll tmp = dfs(v, min(w, flow - used));used += tmp, e[i].w -= tmp, e[i ^ 1].w += tmp;if(used == flow) break;}} vis[u] = false; return used;}pir dinic() {ll f = 0, c = 0;while(spfa()) {for(ll i = 1; i <= n; i++) cur[i] = head[i];f += dfs(s, inf);}for(ll i = 3; i <= tot; i += 2) c -= e[i].w * e[i].c;return mkp(f, c);}void clr() {for(ll i = 1; i <= n; i++) head[i] = 0;tot = 1, n = s = t = 0;}
} G;bool check(ll mid) {G.clr(), G.ins(2 * n + 3, n + 2, inf, mid); sum = 0;for(ll i = 1; i <= 2 * n + 4; i++) deg[i] = 0;for(ll i = 1; i <= n; i++) {G.ins(i + n + 2, i, inf, 0), G.ins(i, i + n + 2, c[i], t[i]);deg[i + n + 2] += c[i], deg[i] -= c[i];sum += c[i] * t[i];}for(ll i = 1; i <= n; i++)G.ins(i, 2 * n + 3, inf, 0), G.ins(n + 2, i + n + 2, inf, 0);for(ll u = 1; u <= n; u++)for(ll v = 1; v <= n; v++)if(a[u][v]) G.ins(v, u + n + 2, inf, 0);ll S = 2 * n + 5, T = S + 1, g = 0;G.n = G.t = T, G.s = S;for(ll i = 1; i <= 2 * n + 4; i++)if(deg[i] < 0) G.ins(S, i, -deg[i], 0), g -= deg[i];else if(deg[i] > 0) G.ins(i, T, deg[i], 0);pir res = G.dinic();return res.fi == g && sum - res.se <= m;
}int main() {rd(n);for(ll i = 1; i <= n; i++) {scanf("%s", str[i] + 1);for(ll j = 1; j <= n; j++)if(str[i][j] == '1') a[j][i] = 1;} rd(n);for(ll i = 1; i <= n; i++) rd(t[i]);rd(n);for(ll i = 1; i <= n; i++) rd(c[i]);rd(m);ll lo = 0, hi = 1250;while(lo <= hi) {ll mid = lo + hi >> 1;if(check(mid)) hi = mid - 1;else lo = mid + 1;} printf("%lld\n", lo);return 0;
}