Codeforces Round 894 (Div. 3) F. Magic Will Save the World 【DP枚举的艺术】

背包问题变式

我们先来看原文题解

First, let’s note that Vika can defeat all the monsters at once, in the last second. There is no point in spending mana gradually.
Now, let’s say we know how many seconds Vika will accumulate mana before spending it. Then we also know how much mana she will have accumulated by that time. How should she spend it?
Note that the total strength of the monsters is given to us. Therefore, it is enough for us to spend as much of the available water mana as possible, so that there is enough fire mana left for the remaining monsters. This is a well-known knapsack problem.
Finally, let’s note that we don’t need to iterate over the number of seconds and build the knapsack each time. It is enough to build it initially, and then iterate over how much water mana we will spend and whether we will have enough fire mana left for the rest.

提取一下关键信息

  1. 我们不需要去规划在什么时候用魔法,因为最后可以把魔法一口气用完
  2. 我们目标是要求第几秒,但是如果给你秒数,你也很难有个策略把水魔法和火魔法完美的分配到每个怪物身上
  3. 假设所有怪物的力量之和是SUM,要明确的是,一定存在一部分A,会被水魔法击败,另外的一部分的B,会被火魔法击败。
  4. 如果我们已知被水魔法击败的怪物力量之和A_SUM, 就可以推出时间,从而推断出B_SUM,就能得到我们花了多少时间
  5. 关键就在这里,枚举的艺术!! 我们可以用dp背包的经典套路,得到怪物血量之和的组合是否存在
  6. 我们最后再从0开始枚举所有血量,即A_SUM,得到最后的情况!
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N = 1e8;
void solve()
{
    ll w, f;
    cin >> w >> f;
    int n;
    cin >> n;
    vector<int> v(n + 1);
    ll sum = 0;
    for (int i = 1; i <= n; i++)
    {
        cin >> v[i];
        sum += v[i];
    }
    vector<int> dp(sum + 1);
    dp[0] = 1;
    for (int i = 1; i <= n; i++)
    {
        for (int j = sum; j >= v[i]; j--)
        {
            dp[j] |= dp[j] || dp[j - v[i]];
        }
    }
    ll res = LLONG_MAX;
    for (int i = 0; i <= sum; i++)
    {
        if (dp[i])
        {
            res = min(res, max((i + w - 1) / w, (sum - i + f - 1) / f));
        }
    }
    cout << res << "\n";
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t;
    cin >> t;
    while (t--)
    {
        solve();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值