题解:P1860 新魔法药水

· · 题解

赛时已严肃被创飞,于是写个题解加深一下记忆。

输出 0 能拿 10 分。

非常鬼畜的 DP。

为了方便表述,我们使用 w_i 表示药品 i 的收益,r_icnt_irs_{i,j}(1\le j \le cnt_i) 表示第 i 次魔法的成品,原料数量,原料编号。

我们想到可以设计状态 f_{i,j,k} 表示前 i 个药品使用 j 次魔法,成本为 k 的最大收益,但这貌似不太好直接转移,所以我们想到可以再算一个 c_{i, j} 表示第 i 个药品用 j 次魔法的最小成本,那么 f_{i,j,k} 的转移就很容易写了,为

再考虑一下 $c_{i,j}$ 怎么算,发现 $c_{i,j}$ 也不好直接计算出来,于是再用一次 DP 用于计算 $c_{i,j}$。设 $g_{i, j}$ 表示当前魔法配方中前 $i$ 个原料使用 $j$ 次魔法的最小成本,$g_{i,j}$ 的转移方程就非常好写了,对于每种魔法,为 $\large g_{i, j} = \min \{ g_{i-1,j-k} + c_{rs_{x,i}, k} \}$, 其中 $0 \le k \le j$,$0\le j \le t - 1$,$t$ 表示的是总魔法次数,$t - 1$ 是因为最后一次还是要用一次魔法,$x$ 表示魔法编号,这个时候的 $\large c_{rs_{x,i}, k}$ 一定是已经算出来的(想想为啥)。 然后,我们就可以得出 $\large c_{r_x, t} = g_{cnt_i, t - 1}$($x$ 和 $t$ 的意义同上)。 还没完,我们发现各种魔法的产物有可能是相同的,所以 $c_{r_x,t}$ 还要取一个最小值才可以。 然后 $f_{i,j,k}$ 是可以压掉一维的,因为其实就是一个变种背包。对于最后的答案取一个最大值输出即可。 然后就没有然后了。 :::success[AC Code] ``` #include <bits/stdc++.h> using namespace std; #define int long long #define rep(i, s, t) for (int i = s; i <= t; i++) #define pre(i, s, t) for (int i = s; i >= t; i--) const int N = 80, M = 300, K = 40, V = 1010, INF = 2e9; int n, m, v, k, w[N], c[N][K], f[M][V], g[N][K], r[M], cnt[M], rs[M][N]; // w_i 第 i 个药品的回收价 r_i, cnt_i, rs_{i, j} 第 i 次魔法的成品,原料数,原料编号 // c_{i, j} 第 i 个药品使用 j 次魔法时的最小成本 // f_{i, j, k} 前 i 个药品用 j 次魔法花 k 元的最大收益(可以压一维) // g_{i, j} 辅助数组,表示当前魔法配方中前 i 个原料使用 j 次魔法的最小成本 signed main() { ios::sync_with_stdio(0); cin.tie(0), cout.tie(0); cin >> n >> m >> v >> k; rep(i, 1, n) cin >> c[i][0] >> w[i]; rep(i, 1, n) rep(j, 1, k) c[i][j] = INF; rep(i, 0, k) rep(j, 0, v) f[i][j] = -INF; f[0][0] = 0; rep(i, 1, m) { cin >> r[i] >> cnt[i]; rep(j, 1, cnt[i]) cin >> rs[i][j]; } rep(t, 1, k) rep(i, 1, m) { rep(j, 1, cnt[i]) rep(s, 0, t - 1) g[j][s] = INF; rep(j, 1, cnt[i]) rep(s, 0, t - 1) rep(ps, 0, s) g[j][s] = min(g[j][s], g[j - 1][s - ps] + c[rs[i][j]][ps]); c[r[i]][t] = min(c[r[i]][t], g[cnt[i]][t - 1]); // 最后还要用一次魔法! } rep(i, 1, n) rep(j, 0, k) rep(l, 0, j) rep(s, c[i][l], v) f[j][s] = max(f[j][s], f[j - l][s - c[i][l]] + w[i] - c[i][l]); int res = 0; rep(i, 0, k) rep(j, 0, v) res = max(res, f[i][j]); return cout << res, 0; } ``` ::: 这题的 DP 是真的复杂,给能想到的大佬磕一个了。。。