AtCoder Beginner Contest 054 D - Mixing Experiment
問題
問題概要
薬品がN個あり、i個目の薬品はタイプAの薬品をa[i-1]グラム、タイプBの薬品をb[i-1]グラム含み、価格はc[i-1]である。
これらの薬品のうちからいくつか選んで混合し、タイプAとタイプBの薬品の混合比がMa:Mbとなるようにしたときの最小予算を求めよ。
解法
ナップサックdpに似ている。
dp[i][j][k] : i番目(1-index)の薬品まで見てタイプAがjグラム、タイプBがkグラム混合されるようにしたときの最小予算。
0≦i≦N、0≦j≦Σa[i]、0≦k≦Σb[i]
今回は「~以内」ではなく「ちょうど~」なのでそのような組み合わせが存在しないことがある。この時のことも考えなくてはいけないため場合分けが多くなる。
最小値を求めたいのでdpはINFで初期化する。
dp[i][j][k]=INFの時はi番目まで見てタイプAがjグラム、タイプBがkグラム混合されるように選ぶ方法はないということである。
i番目を混合するか混合しないかの2通りある。
i番目を混合しないとき
dp[i][j][k]=dp[i-1][j][k]
i番目を混合するとき
dp[i][j][k]=dp[i-1][j-a[i-1]][k-b[i-1]]+c[i-1]
これらのうちの小さいほうである
ただしj-a[i-1]<0またはk-b[i-1]<0の時は配列外参照になるため
dp[i][j][k]=dp[i-1][j][k]となる。
また、dp[i-1][j][k]=INFかつdp[i-1][j-a[i-1]][k-b[i-1]]=INFの時は
dp[i][j][k]も作れないのでINFとなる。
i=0の時は
j=0かつk=0の時はdp[i][j][k]=0となり、
それ以外の時は作れないのでdp[i][j][k]=INFとなる。
答えはi=Nでj:k=Ma:Mbとなるdp[i][j][k]のうちの最小値である。
ソースコード
signed main() { int N, Ma, Mb; cin >> N >> Ma >> Mb; vector<int>a(N); vector<int>b(N); vector<int>c(N); rep(i, N) { cin >> a[i] >> b[i] >> c[i]; } int Sa = 0; int Sb = 0; rep(i, N) { Sa += a[i]; Sb += b[i]; } Sa++; Sb++; vector<vector<vector<int>>>dp(N + 1, vector<vector<int>>(Sa, vector<int>(Sb,INF))); rep(i, N + 1) { rep(j, Sa) { rep(k, Sb) { if (i == 0) { if (j == 0 && k == 0) { dp[i][j][k] = 0; } } else { if (j - a[i - 1] < 0 || k - b[i - 1] < 0) { dp[i][j][k] = dp[i - 1][j][k]; } else { if (dp[i - 1][j][k] == INF && dp[i - 1][j - a[i - 1]][k - b[i - 1]] == INF) { dp[i][j][k] = INF; } else { dp[i][j][k] = min(dp[i - 1][j][k], dp[i - 1][j - a[i - 1]][k - b[i - 1]] + c[i - 1]); } } } } } } int ans = INF; rep(i, N + 1) { rep(j, Sa) { rep(k, Sb) { if (i == N && j * Mb == k * Ma &&j!=0&&k!=0) { ans = min(ans, dp[i][j][k]); } } } } if (ans == INF) { cout << -1 << enld; } else { cout << ans << endl; } }