ABC054-D:Mixing Experiment(400)
問題
https://beta.atcoder.jp/contests/abc054/tasks/abc054_d
N 個の薬品があり、それぞれ a_i, b_i, c_i で与えられる。各 i は1回まで選択できる。Σ a_i : Σ b_i = M_a : M_b となるようにいくつか薬品を選択するときの最小の Σ c_i を求めよ。
制約
- 1 <= N <= 40
- 1 <= a_i, b_i <= 10
- 1 <= c_i <= 100
- 1 <= M_a, M_b <= 10
- 1 <= gcd(M_a, M_b) = 1
- 入力値は整数
考え方
通常のナップザックDPと同じである。i 個選択してタイプAの合計 A, タイプBの合計 B である状態を f (i, A, B) とし、そのときの状態の遷移を考えると、
(1)薬品 i を選択したとき
f (i, A, B) は f (i+1, A+a_i, B+b_i) に遷移する
(2)薬品 i を選択しないとき
f (i, A, B) は f (i+1, A, B) に遷移する
ことがわかる。後は 薬品を N 個入れたときのタイプAとタイプBの値をチェックし、Ma : Mbの比の条件を満たしているときの金額の最小値を選択すれば良い。
int n, Ma, Mb; int[] a, b, c; public void solve(int testNumber, InputReader in, PrintWriter out) { n = in.nextInt(); Ma = in.nextInt(); Mb = in.nextInt(); a = new int[n]; b = new int[n]; c = new int[n]; for (int i = 0; i < n; i++) { a[i] = in.nextInt(); b[i] = in.nextInt(); c[i] = in.nextInt(); } int maxA = Arrays.stream(a).sum(); int maxB = Arrays.stream(b).sum(); int[][][] dp = new int[n+1][maxA+1][maxB+1]; for (int i = 0; i < n; i++) { for (int j = 0; j <= maxA; j++) { for (int k = 0; k <= maxB; k++) { dp[i][j][k] = INF; } } } dp[0][0][0] = 0; for (int i = 0; i < n; i++) { for (int j = 0; j <= maxA; j++) { for (int k = 0; k <= maxB; k++) { if (j >= a[i] && k >= b[i]) { dp[i+1][j][k] = Math.min(dp[i][j-a[i]][k-b[i]]+c[i], dp[i][j][k]); } else { dp[i+1][j][k] = dp[i][j][k]; } } } } int ans = INF; for (int sa = 1; sa <= maxA; sa++) { for (int sb = 1; sb <= maxB; sb++) { if (sa * Mb == sb * Ma) { ans = Math.min(ans, dp[n][sa][sb]); } } } out.println(ans == INF ? -1 : ans); }
ポイント
- 状態の遷移を式であわらす
- 次元をイメージしない(3次元とかイメージすると辛い、4次元以上はできないので)