1449. 数位成本和为目标值的最大数字
https://leetcode-cn.com/problems/form-largest-integer-with-digits-that-add-up-to-target/
题目描述
前置知识
数组
动态规划
背包问题
公司
暂无
思路
由于数组可以重复选择,因此这是一个完全背包问题。
01 背包
对于 01 背包问题,我们的套路是:
而一般我们为了处理边界问题,我们一般会这么写代码:
其中 dp[j] 表示只能选择前 i 个物品,背包容量为 j 的情况下,能够获得的最大价值。
dp[j] 不是没 i 么? 其实我这里 i 指的是 dp[j]当前所处的循环中的 i 值
完全背包问题
回到问题,我们这是完全背包问题:
为什么 01 背包需要倒序,而完全背包则不可以
实际上,这是一个骚操作,我来详细给你讲一下。
其实要回答这个问题,我要先将 01 背包和完全背包退化二维的情况。
对于 01 背包:
注意等号左边是 i,右边是 i - 1,这很好理解,因为 i 只能取一次嘛。
那么如果我们不降序遍历会怎么样呢?
如图橙色部分表示已经遍历的部分,而让我们去用[j - cost[i - 1]] 往前面回溯的时候,实际上回溯的是 dp[i]j - cost[i - 1]],而不是 dp[i - 1]j - cost[i - 1]]。
如果是降序就可以了,如图:
这个明白的话,我们继续思考为什么完全背包就要不降序了呢?
我们还是像上面一样写出二维的代码:
由于 i 可以取无数次,那么正序遍历正好可以满足,如上图。
恰好装满 VS 可以不装满
题目有两种可能,一种是要求背包恰好装满,一种是可以不装满(只要不超过容量就行)。而本题是要求恰好装满
的。而这两种情况仅仅影响我们dp数组初始化
。
恰好装满。只需要初始化 dp[0] 为 0, 其他初始化为负数即可。
可以不装满。 只需要全部初始化为 0,即可,
原因很简单,我多次强调过 dp 数组本质上是记录了一个个自问题。 dp[0]是一个子问题,dp[1]是一个子问题。。。
有了上面的知识就不难理解了。 初始化的时候,我们还没有进行任何选择,那么也就是说 dp[0] = 0,因为我们可以通过什么都不选达到最大值 0。而 dp[1],dp[2]...则在当前什么都不选的情况下无法达成,也就是无解,因为为了区分,我们可以用负数来表示,当然你可以用任何可以区分的东西表示,比如 None。
回到本题
而这道题和普通的完全背包不一样,这个是选择一个组成的最大数。由小学数学知识一个数字的全排列中,按照数字降序排列是最大的
,我这里用了一个骚操作,那就是 cost 从后往前遍历,因为后面表示的数字大。
代码
复杂度分析
时间复杂度:$O(target))$
空间复杂度:$O(target)$
扩展
最后贴几个我写过的背包问题,让大家看看历史是多么的相似。
这里内外循环和本题正好是反的,我只是为了"秀技"(好玩),实际上在这里对答案并不影响。
这里内外循环和本题正好是反的,但是这里必须这么做,否则结果是不对的,具体可以点进去链接看我那个题解
所以这两层循环的位置起的实际作用是什么? 代表的含义有什么不同?
本质上:
这种情况选择物品 1 和物品 3(随便举的例子),是一种方式。选择物品 3 个物品 1(注意是有顺序的)是同一种方式。 原因在于你是固定物品,去扫描容量。
而:
这种情况选择物品 1 和物品 3(随便举的例子),是一种方式。选择物品 3 个物品 1(注意是有顺序的)也是一种方式。原因在于你是固定容量,去扫描物品。
因此总的来说,如果你认为[1,3]和[3,1]是一种,那么就用方法 1 的遍历,否则用方法 2。
大家对此有何看法,欢迎给我留言,我有时间都会一一查看回答。更多算法套路可以访问我的 LeetCode 题解仓库:https://github.com/azl397985856/leetcode 。 目前已经 37K star 啦。 大家也可以关注我的公众号《力扣加加》带你啃下算法这块硬骨头。
最后更新于