问题描述
你在一个水果超市里,货架上摆满了玲琅满目的奇珍异果。
给你一个下标从 1 开始的数组 prices
,其中 prices[i]
表示你购买第 i
个水果需要花费的金币数目。
水果超市有如下促销活动:
- 如果你花费
price[i]
购买了水果i
,那么接下来的i
个水果你都可以免费获得。
注意 ,即使你 可以 免费获得水果 j
,你仍然可以花费 prices[j]
个金币去购买它以便能免费获得接下来的 j
个水果。
请你返回获得所有水果所需要的 最少 金币数。
示例
输入:prices = [3,1,2] 输出:4 解释:你可以按如下方法获得所有水果: - 花 3 个金币购买水果 1 ,然后免费获得水果 2 。 - 花 1 个金币购买水果 2 ,然后免费获得水果 3 。 - 免费获得水果 3 。 注意,虽然你可以免费获得水果 2 ,但你还是花 1 个金币去购买它,因为这样的总花费最少。 购买所有水果需要最少花费 4 个金币。输入:prices = [1,10,1,1] 输出:2 解释:你可以按如下方法获得所有水果: - 花 1 个金币购买水果 1 ,然后免费获得水果 2 。 - 免费获得水果 2 。 - 花 1 个金币购买水果 3 ,然后免费获得水果 4 。 - 免费获得水果 4 。 购买所有水果需要最少花费 2 个金币。
题目解释
1.需要买下所有水果,每个水果有一个价格prices[i],下标从1-n
2.买下第 i 个水果,后面的 i 个水果都可以免费获得,即 下标从i+1到2*i的水果都是可以免费获得的。
3.可以购买免费的水果j,为了免费获得后面的j个水果。
思路引入
1.当前的水果i,那么下一个要买的水果是i+1到2i+1中的一个,因为2i+1位置的水果必须要买,也可以选择购买i+1到2i+1中的一个免费获得2*i+1位置的水果
2.必须从后往前递推,因为从前往后递推的话,到一个当前位置i,不知道之前的水果哪个是买的哪个是免费获得的。
3.如果2*i>=n,然后购买了水果i,那么后面全部的水果都可以全部免费得到。
解题步骤
1.定义dp数组
dp[i]表示买第i-n个水果所需要花费的最少金币数。
2.递推公式:
因为买了i,下一个水果应该在i+1到2i+1中选一个,所以应该在i+1到2i+1中选一个j,此时dp[i] = dp[j]+prices[i-1] (因为下标从1开始,prices还是普通的从0开始的数组,所以要i-1)
因为是要求需要花费的最少金币数,所以要找到一个j,使得最小的dp[i]的值最小。
即
ans = 999999
for j in range(i+1,2*i+2):
ans = min(dp[j]+prices[i-1])
dp[i] = ans
可以用切片简化为寻找一个最小的dp[j],再加上prices[i-1]的值,就等于dp[i]
dp[i] = min(dp[i+1:2*i+2])+prices[i-1]
需要注意的是,当2*i>=n时,买了水果i可以不用管后面的所有水果,此时
dp[i] =prices[i-1]
3.初始化
因为在遍历过程中,从n-1的水果都遍历到了,所以不用另外初始化
4.遍历顺序
因为dp[i]的值依赖于dp[i+1]到dp[2*i+1],所以遍历顺序应该从后往前遍历
5.打印dp数组
对于 prices = [3,1,2],dp数组如下
[0, 0, 0, 2] 对于第i=3,i*2>=n,买第3个水果,dp[3] = prices[3-1]
[0, 0, 1, 2] 对于第i=2,i*2>=n,买第2个水果,dp[2] = prices[2-1]
[0, 4, 1, 2] 对于第i=1,在dp[i+1,2*i+2]中选择最小值,dp[1] = min(dp[i+1:2*i+2])再加上prices[1-1]
完整代码
class Solution:
def minimumCoins(self, prices: List[int]) -> int:
n = len(prices)
dp = [0]*(n+1)
for i in range(n,0,-1):
if i*2>=n:
dp[i] = prices[i-1]
else:
dp[i] = min(dp[i+1:2*i+2])+prices[i-1]
return dp[1]