DP系列

Leecode 53最大子序和 simple

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        if len(nums)==1: return nums[0]
        max_sum = nums[0]    #must两个,一个记录,一个遍历
        cur_sum = max_sum
        for i in range(1,len(nums)):
            if (cur_sum+nums[i])>nums[i]:
                cur_sum=cur_sum+nums[i]
            else:
                cur_sum=nums[i]
            if cur_sum>max_sum: max_sum=cur_sum
        return max_sum

Leecode 300最长上升子序列 median

在这里插入图片描述

class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        if len(nums)<=1:return len(nums)
        mem = [1 for _ in range(len(nums))]  #设定每个点单独为一个子序列
        for j in range(1,len(nums)):
            for i in range(0,j):
                if nums[i]<nums[j]:
                    mem[j]=max(mem[j],mem[i]+1)   #!!!因可以discontinuous,故every new add j都需和之前all i单独相比;符合则加1;否则仍然为mem[j];也就是取max
        return max(mem)

Leecode 322零钱兑换 median

贪心法:不一定能得到全局最优
DP:全局最优,自底向上解决problem
e.g:零钱1,3,4 6=4+1+1<贪心> 6=3+3< dp >

class Solution:
    def coinChange(self, coins , amount ) -> int:
        # 自底向上
        # dp[i] 表示金额为i需要最少的硬币
        # dp[i] = min(dp[i], dp[i - coins[j]]) j所有硬币

        dp = [float('inf')]*(amount+1)
        dp[0]=0  #amount=0,不需要硬币
        for i in range(1,amount+1):
            dp[i] = min(dp[i-c] if 0<=i-c else dp[i]  for c in coins)+1
        return dp[-1] if dp[-1]!=float('inf')  else -1    # 易错点:dp[-1]!='inf' bug,因type(dp[-1])是float,type('inf')是str

0-1背包问题


大问题拆分成小问题,通过寻找大问题与小问题的递推关系,解决一个个小问题,最终达到解决原问题的效果

dp:记忆性 避免了重复计算;核心就在于填表,表填写完毕,最优解也就找到。
填表:(物品个数+1)x(len(weight_most + 1))
注意:dp填表要插入头一行(第0个物品,背包剩余容量all可能)
分治法:在子问题和子子问题等上被重复计算了很多次

0-1背包问题 参考python版
思想:第i个物品放or不放;
放:检测了第i个物品,剩余容积减weight[i],价值增加value[i]
不放:检测了第i个物品,价值不增加
放与不放中取价值max,的措施之一即可在这里插入图片描述

weight = [2,3,4,5]#表示重量,各个物品
value = [3,4,5,6]#表示价值,各个物品
weight_most = 8  #最大容量

weight.insert(0, 0)  # 前0件要用
value.insert(0, 0)  # 前0件要用
import numpy as np
def bag_0_1(weight, value, weight_most):  # return max value
    num = len(weight)-1  #物品个数
    
    bag = np.zeros([num + 1, weight_most + 1], dtype=np.int32)  # python下标从零开始
    for i in range(1, num + 1):
        for j in range(weight_most,0,-1):
            if weight[i] <= j:
                bag[i][j] = max(bag[i - 1][j - weight[i]] + value[i], bag[i - 1][j])
            else:
                bag[i][j] = bag[i - 1][j]
    return bag
result = bag_0_1(weight, value, weight_most)  #整个表格
print(result[-1,-1])  #get最大价值
#find 路径----回溯
num = len(weight)-1  #物品个数
item=[0]*(num+1)  #为路径表示分配内存
def find_item(i,j,item):
    if i>=0:
        if res[i,j]==res[i-1,j]:
            item=find_item(i-1,j,item)
        elif j-weight[i]>=0 and res[i,j]==res[i-1,j-weight[i]]+value[i]:
            item[i]=1
            item =find_item(i-1,j-weight[i],item)
    return item

item = find_item(num,weight_most,item)
print(item)

%分割线----------------------------------------------------------------------------------------------------%
Leecode 416分割等和子集 median
在这里插入图片描述
时间复杂度:O(n*(target/2))
空间复杂度:O(n*(target/2))
#return是bool,不是‘False’–string

class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        target = sum(nums)
        if target % 2 != 0: return False
        target //= 2  # 取和一半

        n = len(nums)
        dp = [[False] * (target + 1) for _ in range(n)]
        # dp[0][0]=True
        for j in range(target + 1):  # 第一行 拎出来 打底
            if j >= nums[0]:
                dp[0][j] = True
                break

        for i in range(1, n):
            for j in range(target + 1):
                if nums[i] <= j:
                    dp[i][j] = dp[i - 1][j] or dp[i - 1][j - nums[i]]
                else:          #一定要有
                    dp[i][j] = dp[i - 1][j]
        return dp[-1][-1]

时间复杂度:O(n*(target/2))
空间复杂度:O(target/2)

#参考,不强制学习
class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        target = sum(nums)
        if target % 2 != 0: return False
        target //= 2  # 取和一半

        n = len(nums)
        dp = [[False] * (target + 1) for _ in range(n)]
        # dp[0][0]=True

        pre=[False]*(target + 1)
        cur=[False]*(target + 1)
        pre[0]=True

        for j in range(target + 1):  # 第一行 拎出来 打底
            if j >= nums[0]:
                pre[j]= True
                break
        #隐含pre=dp[i-1]行, cur=dp[i]行
        for i in range(1, n):
            for j in range(target + 1):
                if nums[i] <= j:
                    cur[j]=pre[j] or pre[j-nums[i]]                   
                else:          #一定要有
                    cur[j] = pre[j]
        return cur[-1]

Leecode 1049最后一块石头的重量 II median

#dp[i][j]代表选i个石头的最大重量j和
# 其中容量为sum/2, 即求最大的j<sum/2
class Solution:
    def lastStoneWeightII(self, stones: List[int]) -> int:
        total, n = sum(stones), len(stones)
        dp = [[0]*(total//2+1) for _ in range(n)]
        for j in range((total//2+1)):
            if j>=stones[0]:
                dp[0][j]=stones[0]
                #break   #break是bug一定不能有
        for i in range(1, n):
            for j in range(0, total//2+1):
                if stones[i]<=j: # 这个代表选第i个, 然后再去比较选之后的大小
                    dp[i][j] = max(dp[i-1][j], dp[i-1][j-stones[i]]+stones[i])
                else:
                    dp[i][j] = dp[i-1][j] # 这个代表不选第i个
        return total-2*dp[-1][-1]

#方法二   不用单独拎出来第一行
class Solution:
    def lastStoneWeightII(self, stones: List[int]) -> int:
        total, l = sum(stones), len(stones)
        dp = [[0]*(total//2+1) for _ in range(l+1)]
        for i in range(1, l+1):
            for j in range(1, total//2+1):
                if stones[i-1]<=j: # 这个代表选第i个, 然后再去比较选之后的大小
                    dp[i][j] = max(dp[i-1][j], dp[i-1][j-stones[i-1]]+stones[i-1])
                else: dp[i][j] = dp[i-1][j] # 这个代表不选第i个
        return total-2*dp[-1][-1]
      

参考地址:https://leetcode-cn.com/problems/last-stone-weight-ii/solution/0-1bei-bao-by-yaosw/

Leecode 1046最后一块石头的重量 I simple
每一回合,从中选出两块 最重的 石头


#取负   建堆-弹出最大值-维护堆结构-弹出最大值--摩擦结果push回堆--循环
class Solution:
    def lastStoneWeight(self, stones: List[int]) -> int:
        import heapq  ## heapq库是专门处理最小堆的库
        stones_heap = [-i for i in stones]  ## 用取负数的办法弯道处理最大堆问题
        heapq.heapify(stones_heap)  ## 把list最小推结构化;时间复杂度是O(n)
                                    # 直接建堆

        while len(stones_heap) > 1:  ## 当至少还有2块石头的时候
            a = heapq.heappop(stones_heap)  #弹出max后,auto维护## 取出质量最大的石头(因为是负数,所以值是最小);时间复杂度是O(logn),因为取出后还要用logn的时间保持堆结构
            b = heapq.heappop(stones_heap)  ## 在剩下的石头里取出质量最大的石头;时间复杂度是O(logn)
            if a < b:  ## 如果第一块石头比第二块重,那么把摩擦剩下的小石头放进堆里
                heapq.heappush(stones_heap, a - b)  ## 如果两块石头一样重,就都没有了
        if stones_heap:
            res = -stones_heap[0]  ## 如果剩下石头,那么返回值是其质量
        else:
            res =  0  ## 如果不剩下石头,那么返回值是0
        return res
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值