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

146 阅读1分钟

题目链接

英雄升级与奖励最大化 - MarsCode

题目分析

在本题中,我们需要解决两个问题:

  1. 第一个是计算每个英雄达到 b[i]b[i] 能力值时的最少升级次数。
  2. 第二个是计算最多进行 kk 次升级操作后,能获得的最大奖励总和。

解题思路

对于第一个问题,每次升级的方式为 a[i]=a[i]+a[i]/xa[i]=a[i]+⌊a[i]/x⌋ ,x取值范围为 [1,a[i]][1,a[i]] 。 我们可以使用广度优先搜索,计算到每一个能力值的最少升级次数。在计算 a[i]/x⌊a[i]/x⌋ 时,我们可以直接枚举 xx ,但此时时间复杂度为 O(n)O(n) ,可能会超时。可以使用数论分块的方式计算。

数论分块:我们假设 a[i]a[i] 为 15,那么 a[i]/x⌊a[i]/x⌋ 的取值有哪些呢?可以列举出来:

15 / 1 = 15
15 / 2 = 7
15 / 3 = 5
15 / 4 = 4
15 / 5 = 3
15 / 6 = 2
15 / 7 = 2
15 / 8 = 1
15 / 9 = 1
15 / 10 = 1
15 / 11 = 1
15 / 12 = 1
15 / 13 = 1
15 / 14 = 1
15 / 15 = 1

可以发现有大量重复的值,除去重复的值最多有 n\sqrt{n} 个。 计算这些重复的值,可以通过以下代码实现:

l, r = 1, n
while l <= x:
    d = x // l
    r = x // d
    # 此时表示区间 [l,r] 所有的数除 n 的结果为 d
    l = r + 1

对于第二个问题,这符合动态规划的01背包模型,即:有 n 个物品,每个物品的重量为 w[i]w[i],价值为 v[i]v[i] ,现有一个容量为 WW 的背包,从选择若干个,在总重量不超过 WW 的情况下最大化总价值。

动态规划转移方程如下:

f[i][j]=max{f[i1][j]f[i1][jw[i]]+v[i] if j>=w[i]f[i][j]=\max\begin{cases} f[i-1][j] \\ f[i-1][j-w[i]]+v[i]\ \text{if}\ j >= w[i] \\ \end{cases}

可以使用滚动数组的方式优化空间复杂度,注意此时要倒着枚举 jj 来转移状态:

f[j]=max{f[j]f[jw[i]]+v[i] if j>=w[i]f[j]=\max\begin{cases} f[j] \\ f[j-w[i]]+v[i]\ \text{if}\ j >= w[i] \\ \end{cases}

在本问题中,升级次数为重量,奖励为价值。

具体代码实现

以下是 python 的代码实现:

from collections import deque

N = int(1e5)
w = [1e9] * N


def bfs():
    """
    使用广度优先搜索(BFS)算法来更新从起点到每个节点的最短距离。
    该函数没有返回值,但会修改全局数组w,以存储从起点到每个节点的最短距离。
    """
    # 初始化队列q,用于存储待处理的节点及其对应的步数
    q = deque()
    # 设置起点的步数为0
    w[1] = 0
    # 将起点及其步数加入队列
    q.append((1, 0))
    # 当队列不为空时,循环处理队列中的元素
    while q:
        # 从队列中取出一个节点及其步数
        x, y = q.popleft()
        # 初始化l和r,用于计算x的除数范围
        l, r = 1, 1
        # 遍历x的所有除数
        while l <= x:
            # 计算x除以l的商
            d = x // l
            # 计算x除以商的值,以确定除数的范围
            r = x // d
            # 如果x加上商小于N,并且当前步数加1小于已记录的到达x+d的最小步数
            if x + d < N and w[x + d] > y + 1:
                # 更新到达x+d的最小步数
                w[x + d] = y + 1
                # 将x+d及其步数加入队列
                q.append((x + d, y + 1)) 
            # 更新l为下一个除数范围的起始值
            l = r + 1

def solution(n, k, b, c):
    """
    参数:
    n -- 物品的数量
    k -- 背包的容量
    b -- 物品的重量列表
    c -- 物品的价值列表
    
    返回:
    返回背包能装下的最大价值。
    """
    # 初始化动态规划数组,长度为背包容量加一
    f = [0] * (k + 1)
    
    # 更新物品重量列表
    for i in range(n):
        b[i] = w[b[i]]
    
    # 使用动态规划填充背包,计算每个容量下的最大价值
    for i in range(n):
        for j in range(k, b[i] - 1, -1):
            # 对于每个物品,选择放入或不放入背包,取价值较大的情况更新动态规划数组
            f[j] = max(f[j], f[j - b[i]] + c[i])
    
    # 返回背包最大容量下的最大价值
    return f[k]

if __name__ == "__main__":
    # Add your test cases here
    print(solution(4, 4, [1, 7, 5, 2], [2, 6, 5, 2]) == 9)
    print(solution(3, 0, [3, 5, 2], [5, 4, 7]) == 0)

关键步骤和细节

  1. 首先,我们计算每个英雄达到 b[i]b[i] 能力值时的最少升级次数。
  2. 使用广度优先搜索,计算到每一个能力值的最少升级次数。
  3. 使用数论分块的方式计算 a[i]/x⌊a[i]/x⌋ 的取值。
  4. 动态规划求解背包问题,计算每个容量下的最大价值。
  5. 返回背包最大容量下的最大价值。

复杂度分析

  1. 广度优先搜索的时间复杂度为 O(n)O(n),其中 nn 是升级的次数。
  2. 对于每个物品,使用数论分块计算 a[i]/x⌊a[i]/x⌋ 的取值,时间复杂度为 O(n)O(\sqrt{n}), 其中 nna[i]a[i]
  3. 对于动态规划求解背包问题,时间复杂度为 O(nk)O(nk),其中 nn 是物品的数量,kk 是背包的容量。
  4. 动态规划需要 O(k)O(k) 的空间来存储每个容量下的最大价值。

总结

本题的关键是理解问题的要求和限制,以及动态规划的求解过程。首先,我们需要计算每个英雄达到 b[i]b[i] 能力值时的最少升级次数,可以使用广度优先搜索来实现,其中结合数论分块来优化转移次数。最后,我们需要使用动态规划求解背包问题,计算每个容量下的最大价值。