英雄升级与奖励最大化| 豆包MarsCode AI 刷题

5 阅读5分钟

问题描述:

问题描述

在一个游戏中,小W拥有 n 个英雄,每个英雄的初始能力值均为 1。她可以通过升级操作来提升英雄的能力值,最多可以进行 k 次升级。

每次升级操作包含以下步骤:

  1. 选择一个英雄

  2. 选择一个正整数 x

  3. 将该英雄的能力值 aiai​ 更新为:ai=ai+⌊ai/x⌋ai​=ai​+⌊ai​/x⌋

    • 其中 ⌊ ⌋ 表示向下取整操作

游戏规则:

  • 当英雄的能力值首次达到或超过目标值 bibi​ 时,小W可以获得对应的奖励 cici​
  • 每个英雄的奖励只能获得一次
  • 升级操作的选择是自由的,可以多次选择同一个英雄进行升级

请计算在最多进行 k 次升级操作后,小W能获得的最大奖励总和。

测试样例:

样例1:

输入:n = 4 ,k = 4 ,b = [1, 7, 5, 2] ,c = [2, 6, 5, 2]
输出:9
解释:可以通过以下操作获得最大奖励:

  1. 第一个英雄初始值为 1,已达到目标值 1,直接获得奖励2
  2. 第四个英雄初始值为 1,选择 x=1 升级一次变为 2,达到目标值2,获得奖励2
  3. 第三个英雄通过三次合理的升级操作 (1->2->4->5) 可达到目标值5,获得奖励5
    总奖励为:2 + 2 + 5 = 9

样例2:

输入:n = 3 ,k = 0 ,b = [3, 5, 2] ,c = [5, 4, 7]
输出:0
解释:无法进行升级操作,因此无法获得奖励

样例3:

输入:n = 3 ,k = 3 ,b = [3, 5, 2] ,c = [5, 4, 7]
输出:12
解释:可以通过以下操作获得最大奖励:
可以通过合理的升级操作使第一个和第三个英雄达到目标值,获得总奖励5 + 7 = 12

解题思路:

我们首先提取题目的关键信息:

  1. 每个英雄初始能力值都是1,要升级到目标能力值,升级次数可能大于等于0(当目标能力值就是1的话不需要升级)。
  2. 当英雄升级到目标能力值时,可以获得对应的奖励。
  3. 我们需要求最多能获得多少奖励
  4. 而我们只有k次升级机会。

基于上面几点,我们需要知道能力值升级到target能力值,最少可以几次升级。这样才能保证我们不浪费升级次数。我们来看一下升级公式:

ai = ai + ⌊ai/x⌋ 其中x是正整数,也就是x至少得大于等于1

x大于ai时,那么⌊ai/x⌋就为0了,相当于没升级还浪费了一次升级机会。 当x等于ai时, 那么⌊ai/x⌋就为1了,相当于升级了1能力。 当x小于ai时, 那么⌊ai/x⌋最大可以为ai(相当于能力翻倍了),最小其实是1(由于是下取整)。

我们可以先计算出每个英雄升级到目标能力值最少花多少升级次数。 我们用f(i,target)表示当前能力值为i,升级到目标能力值target的最少升级次数 如果当前能力值翻倍还比目标能力值小的话,那就翻倍,答案为 1 + f(2 * i,target)。 如果翻倍比目标能力值大,那就取x从2开始往上试探,直到升级完的能力值是小于等于target的。,答案是1 + f(i + ⌊i/x⌋ ,target)。 这个过程我们还可以用记忆化搜索做一些优化。

此时我们已经算完了每个英雄升级到目标能力值花的最少次数了。 我们只需要在里面选一些,保证开销小于等于k,并且得到的奖励最多就行了。 其实这个就是一个背包问题了。

核心

  • 两阶段问题

    1. 能力值扩展:通过 comp2 求得从 1 到 b[i] 的最小步数。
    2. 背包优化:利用这些步数作为重量解决 0-1 背包问题,最大化在不超过总步数的情况下的总价值。
  • 算法策略

    • 记忆化搜索:用于优化能力扩展计算,减少重复计算。
    • 动态规划:用于解决组合优化中的背包问题。
  • 整体流程

    • 先转换问题为求步数的操作,然后将此步数与价值对应到一个背包问题中,再寻求在总步数不超过 k 时的最大价值。

把升级能力当成背包容量,把奖励当成金额。以下提供具体的代码。 其中的01背包代码可以做优化,此处使用的是最朴素的01背包方法。

代码:

#include <unordered_map>
#include <vector>
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
unordered_map<int, int> memo;

int comp2(int curlevel,int target)
{
    if(curlevel==target){
        return 0;
    }
    int hashv = 31 * curlevel + target;
    if(memo.find(hashv)!=memo.end()){
        return memo[hashv];
    }
    if(2*curlevel<=target){
        return memo[hashv]=1+comp2(2*curlevel,target);
    }else{
        int o=2;
        for(;o<=curlevel;++o){
            if((curlevel+ curlevel/o)>target){
                continue;
            }else{
                break;
            }
        }
        return memo[hashv]=1+comp2(curlevel+ curlevel/o,target);
    }
}

int bpack(int k,const vector<int> &weight,const vector<int> &value){
    //dp[i][j] 前i个物品里背包容量为j时最大的奖励
    int n =weight.size();
    vector<vector<int>> dp (n+1,vector<int>(k+1,0));
    for(int i=1;i<n+1;++i){
        for(int j=0;j<k+1;++j){
            dp[i][j]=max(dp[i-1][j],j>=weight[i-1]?(dp[i-1][j-weight[i-1]]+value[i-1]):0);
        }
    }
    return dp[n][k];
}

int solution(int n, int k, const std::vector<int>& b, const std::vector<int>& c) {
    //对每个数达到的能力改成对应的开销次数
    vector<int> times(b.size());
    for(int i=0;i<n;++i){
        times[i]=comp2(1,b[i]);
    }
    //把times当成每个物品的容量,c当初每个物品的价值,背包最大容量为k,01背包
    return bpack(k, times, c);
}

int main() {
    // Add your test cases here
    std::cout << (solution(4, 4, {1, 7, 5, 2}, {2, 6, 5, 2}) == 9) << std::endl;
    std::cout << (solution(3, 0, {3, 5, 2}, {5, 4, 7}) == 0) << std::endl;
    return 0;
}

算法复杂度:

时间复杂度:O(n* log target + n* k)

空间复杂度:O(n * k),dp数组的大小