原题链接:
https://atcoder.jp/contests/abc054/tasks/abc054_d
Problem Statement
Dolphin is planning to generate a small amount of a certain chemical substance C
.
In order to generate the substance C
, he must prepare a solution which is a mixture of two substances
A
A
A and
B
B
B in the ratio of
M
a
M_a
Ma:
M
b
M_b
Mb.
He does not have any stock of chemicals, however, so he will purchase some chemicals at a local pharmacy.
The pharmacy sells
N
N
N kinds of chemicals. For each kind of chemical, there is exactly one package of that chemical in stock.
The package of chemical
i
i
i contains
a
i
a_i
ai grams of the substance
A
A
A and
b
i
b_i
bi grams of the substance
B
B
B, and is sold for
c
i
c_i
ci yen (the currency of Japan).
Dolphin will purchase some of these packages. For some reason, he must use all contents of the purchased packages to generate the substance C
.
Find the minimum amount of money required to generate the substance C
.
If it is not possible to generate the substance C
by purchasing any combination of packages at the pharmacy, report that fact.
Constraints
1
≤
N
≤
40
1 \leq N \leq 40
1≤N≤40
1
≤
a
i
,
b
i
≤
10
1 \leq a_i,b_i \leq 10
1≤ai,bi≤10
1
≤
c
i
≤
100
1 \leq c_i \leq 100
1≤ci≤100
1
≤
M
a
,
M
b
≤
10
1 \leq M_a,M_b \leq 10
1≤Ma,Mb≤10
g
c
d
(
M
a
,
M
b
)
=
1
gcd(M_a,M_b)=1
gcd(Ma,Mb)=1
a
i
,
b
i
,
c
i
,
M
a
a_i, b_i, c_i, M_a
ai,bi,ci,Ma and
M
b
M_b
Mb are integers.
翻译
简单来说,就是有N个药,对于每一种药
i
i
i,内含
A
A
A成分
a
i
a_i
ai克,含
B
B
B成分
b
i
b_i
bi克,每种药只有一个。现在要选择其中几种药,最终得到成分
A
:
B
=
M
a
:
M
b
A:B=M_a:M_b
A:B=Ma:Mb
每种药
i
i
i的价格为
c
i
c_i
ci,求使得总花费最小的选择方案。
分析
求最优化方案的题,如果数据再小点,可以用二进制枚举
暴力解决掉,但是既然
N
N
N大于
30
30
30,那么需要用动态规划
了。
(
2
20
2^{20}
220约等于
1
0
6
10^6
106,
2
30
2^{30}
230约等于
1
0
9
10^9
109,所以当
N
N
N小于
30
30
30时其实可以暴力玄学一波)
状态表示:
dp[i][j][k]
表示只从1~i
中选,其中A
有j
克,B
有k
克的选法中,最小的花费。
递推思路:
1. 不选i
如果不选i
,那么dp[i][j][k]
和dp[i-1][j][k]
的值是一样的。
也就是说,即使从1~i
中选,我也不选i
,那么效果和从1~i-1
中选是一样的。
即
dp[i][j][k] = dp[i-1][j][k];
2. 选i
如果选i
,那么为了使得dp[i][j][k]
表示A
选了j
克,B
选了k
克,所以应该从状态dp[i-1][j-a[i]][k-b[i]]
转移过来。也就是说
dp[i][j][k] = dp[i-1][j-a[i]][k-b[i]] + c[i];
从小到大遍历i
,那么对于每一个i
,只考虑选不选当前的i
就可以了,因为对于i-1
的状态,已经在上一次循环中计算过了,可以拿过来直接用,递推嘛,就是这样的。
3. 所以最终结果就是
dp[i][j][k] = min(dp[i-1][j][k], dp[i-1][j-a[i]][k-b[i]] + c[i]);
但这里需要考虑一点,也就是j<a[i]
或者k<b[i]
的情况
如果发生这种情况,也就意味着如果我选了a[i]
,那么就会必然超过j
,而这肯定不是dp[i][j][k]
要表示的东西,等j
再大点了才能在考虑把a[i]
加进来。
所以这个时候不能选i
,所以直接从dp[i-1][j][k]
转移过来。
所以最终就变成了
if (j < a[i] || k < b[i])
dp[i][j][k] = dp[i - 1][j][k];
else
dp[i][j][k] = min(dp[i - 1][j][k], dp[i - 1][j - a[i]][k - b[i]] + c[i]);
AC代码
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 50;
const int INF = 0x3f3f3f3f;
int dp[N][10 * N][10 * N];
int n, ma, mb;
int a[N];
int b[N];
int c[N];
int main()
{
cin >> n >> ma >> mb;
for (int i = 1; i <= n; ++i)
cin >> a[i] >> b[i] >> c[i];
memset(dp, 0x3f, sizeof(dp));
dp[0][0][0] = 0;
for (int i = 1; i <= n; ++i)
{
for (int j = 0; j <= 400; ++j)
{
for (int k = 0; k <= 400; ++k)
{
if (j < a[i] || k < b[i])
dp[i][j][k] = dp[i - 1][j][k];
else
dp[i][j][k] = min(dp[i - 1][j][k], dp[i - 1][j - a[i]][k - b[i]] + c[i]);
}
}
}
int ans = INF;
for (int i = 1; i <= 400; ++i)
for (int j = 1; j <= 400; ++j)
if (i * mb == j * ma)
ans = min(dp[n][i][j], ans);
if (ans == INF)
cout << -1 << endl;
else
cout << ans << endl;
return 0;
}