AtCoder Beginner Contest 054 D - Mixing Experiment

原题链接:
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 1N40
1 ≤ a i , b i ≤ 10 1 \leq a_i,b_i \leq 10 1ai,bi10
1 ≤ c i ≤ 100 1 \leq c_i \leq 100 1ci100
1 ≤ M a , M b ≤ 10 1 \leq M_a,M_b \leq 10 1Ma,Mb10
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中选,其中Aj克,Bk克的选法中,最小的花费。

递推思路:

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;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值