LC474. 一和零 (动态规划变形背包问题-中等) + 典型背包问题

1、典型背包问题(可参考文章)

题目:给定 n 件物品,物品的所占空间为 w[i],物品的价值为 c[i]。现挑选物品放入背包中,假定背包能承受的最大容量为 V,问应该如何选择装入背包中的物品,使得装入背包中物品的总价值最大?

分析:这是一个典型背包问题是求解 容量一定 的情况下 物品总价值最高的情况
        如:音响 价值3k 容量4    电脑 价值2k 容量3   吉他 价值1.5k 容量1   背包容量为4 (w=[4,3,1] c=[3,2,1.5]) 可以列如下的动态网格:
              0  1    2    3     # 商品个数n
           0 0  0    0    0
           1 0  1.5  1.5  1.5
           2 0  1.5  1.5  1.5
           3 0  1.5  2    2
           4 0  1.5  3.5  3.5
           # 容量情况v
   状态定义:d[i][j] 代表背包容量为i时候 判断当存在第j个物品时(可能装也可能没装)背包的最大价值。
  状态转移方程:  d[i][j] = max(不装该物品时候的价值,该物品的价值+剩余空间的价值) = max(d[i][j-1],c[j-1]+d[i-w[j-1]][j-1])
  初始状态:    为避免讨论  通常多设置一行  可以认为第0个物品的价值为0  

                                             多设置一列 可以认为背包没有空间的情况下

复杂度:  时间复杂度O(vn)  空间复杂度O(vn)

代码:

def findMaxFormBB(W, C, v):
    n = len(W)
    d = [[0 for i in range(n+1)] for j in range(v+1)]
    maxval = 0
    for i in range(1, v+1):
        for j in range(1, n+1):
            if i >= W[j-1]:
                d[i][j] = max(d[i][j-1], C[j-1] + d[i - W[j-1]][j-1])
            else:
                d[i][j] = d[i][j-1]
            maxval = max(maxval, d[i][j])
    return maxval

 

2、变形背包问题(LC474. 一和零

题目:给你一个二进制字符串数组 strs 和两个整数 m 和 n 。请你找出并返回 strs 的最大子集的大小,该子集中 最多 有 m 个 0 和 n 个 1 。
分析:
   问题重述:
            该问题是一个背包问题,当例子为strs = ["10", "0001", "111001", "1", "0"], m = 5, n = 3,可做如下解读:有一组物品(A B C D E)包含S1(0)、S2(1)两个小物品的数量分别为 A:1和1、B:3和1、C:2和4、D:0和1、E:1和0, 问如何取ABCD四个物品使得S1、S2分别小于5和3且物品数量是最多的。
     这道题要满足 两个容量一定的情况下 物品数量 最多的情况    所以需要设计一个三维表格
  状态定义:  d[i][j][k] 代表 S1的数量为i、S2的数量为j时,添加到第k个物品时 物品的总量 (两个容量一点一点的试,物品一点一点的试  讨论的标准是选或者不选)
  状态转移方程:d[i][j][k] = max(不选这个物品时候的数量,选这个物品时的数量) =

                                    max(d[i][j][k-1],1 + d[i-第k物品包含S1的数量][j-第k物品包含S2的数量][k-1])
  初始状态:  为了避免讨论  此处我们认为第0个字符串是空串  故 d[:][:][0] = 0

  注意点:
                         当 S1的数量为0-m 时,都满足情况 故 第一个遍历的范围为 [0,m+1]
                         当 S2的数量为0-n 时,都满足情况 故 第一个遍历的范围为 [0,n+1]

复杂度: 时间复杂度O(mnl) 空间复杂度O(mnl)

PS:   ① 学习动态规划解变形 (三维)01背包问题

         ② 学习计算每个字符串中0 1个数的方法

         ③ 利用 for range初始化  要注意  如果想以 ijk(分别为mnl的遍历下标)访问,则初始化时要按照kji来初始化。

               举个例子   d = [[0]*2 for _ in range(3)]  得到的数组是 3*2的。最后一个元素是d[2][1].

 

代码:

def findMaxForm(strs, m, n):
    l = len(strs)
    d = [[[0 for _ in range(l+1)] for _ in range(n+1)] for _ in range(m+1)]  # 初始化  如果想按照 i j k 访问  怎 按照 k j i初始化
    zeros = [sum(1 for c in s if c == '0') for s in strs]   # 学习计算每个字符串中0 1个数的方法
    ones = [sum(1 for c in s if c == '1') for s in strs]

    for i in range(m+1):        # 注意i,j要从0开始 否则会跳过没有0或没有1的字符串
        for j in range(n+1):
            for k in range(1, l+1):
                # 当 0的上限i大于 当前字符串的0的个数  且 1的上限大于 当前字符串1的个数   注意一下第k个字符串 对应存储其0 1个数的下标
                if i >= zeros[k-1] and j >= ones[k-1]:
                    d[i][j][k] = max(d[i][j][k - 1], 1 + d[i - zeros[k-1]][j - ones[k-1]][k-1])
                else:
                    d[i][j][k] = d[i][j][k - 1]
    return d[-1][-1][-1]

 

路虽远,行则将至。事虽难,做则必成 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值