从英雄升级到01背包

73 阅读3分钟

今天写的第一道题,自认为没有问题,但是测试用例没通过,我感觉应该是用例出错了?

题目如下:

image.png

首先我的理解是:

这题没有对x做出除了正整数以外的限制,同时也不要求能力正好为bi,而是达到或超过bi就行。所以每次都选x为1是升级最快的,k次升级能到2的k次方级。

所以我们把数组b做一个对2取对数然后向上取整的操作之后,这题其实就转化为了一个经典的01背包问题。

代码如下:

import math
def solution(n, k, b, c):
    # Edit your code here
    b=list(map( lambda x:math.ceil(math.log2(x)) , b ))
    dp=[[0]*(k+1)for _ in range(n+1)]
    for i in range(1,n+1):
        dp[i][0]=dp[i-1][0] + (c[i-1] if b[i-1]==0 else 0)
    for i in range(1,n+1):
        for j in range(1,k+1):
            if j<b[i-1]:
                dp[i][j]=dp[i-1][j]
            else:
                dp[i][j]=max(dp[i-1][j],dp[i-1][j-b[i-1]]+c[i-1])
            #print(dp)
    return dp[n][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)

但是奇怪的是,测试用例第一个的答案居然是9而不是10,我不太理解。按理来说,第一个英雄不用升级就达到1级,拿下2分;第二和三个英雄都可以通过3次升级达到8级,但是第二个英雄分高一点,拿下6分;最后第四个英雄升级一次拿下2分,结果应该是2+6+2=10分。我也不知道为啥,难道我对题目理解有误?

顺便简单地说一下01背包吧,也算是让这篇文章有点干货了(虽然我感觉这种基础算法应该各位大佬们早都会了hh)

01背包问题指的是你拥有容量为k的背包,并且有n个物品,i号物品的价值为v[i],要占b[i]的容量,求背包装下的最大价值。每一个物品只有一个,也就是只能装入0/1个,这应该也是01背包名字的由来。另外,如果没有数量限制,就转化为完全背包问题,思路上大差不差,都是动态规划。

基础地,我们可以创建一个(n+1)*(k+1)的dp数组。一共n+1行,代表当把第i个物品纳入考虑时,此时的最大价值。多的这个1指的是额外多加一行,代表当没有物品在考虑范围内的时候,最大价值,这一行显然都为0;一共k+1列,代表当背包容量为j时的最大价值。多的1指的是背包容量为0的时候的最大价值,这个取决于有没有占容量为0的物品。显然,我们要求的就是dp[n][k]。我们可以写一个双层循环,遍历dp数组。对于每一个物品,只有两种可能:放入背包与不放入背包。

首先判断该物品占用容量是不是比背包大:

如果是,那么dp[i][j]=dp[i-1][j],因为无法装入这个物品,那么相当于不把这个物品纳入考虑的情况下的最大值。

如果否,那么这个物品有可能装入背包(当然不是一定会装入),那么如果最优解是不装入,就有dp[i][j]=dp[i-1][j],道理跟上面一样,既然不装这个物品,相当于不考虑;如果装入,那么dp[i][j]=dp[i-1][j-b[i]] + v[i]。至于为什么是i-1呢,因为这是01背包,每个物品只能被取一次。

对于这两种情况,我们取最大值就是最优解:dp[i][j]=max( dp[i-1][j] , dp[i-1][j-b[i]] + v[i] )