动态规划总结

1. 0-1背包

1.1 相关链接

python解决0-1背包问题(超直观)
01背包问题
彻底理解0-1背包问题

1.2 问题描述

  1. 有n件物品,每件物品的重量为w[i],价值为c[i]。现有一个容量为V的背包,问如何选取物品放入背包,使得背包内物品的总价值最大。
  2. 其中每种物品都只有一件。

1.3 解决实现

二维dp

时间复杂度:O(物品个数背包大小)
空间复杂度:O(物品个数
背包大小)

令dp[i][j]来表示前i件物品装入容量为j的背包所能得到的最大总价值。

对于dp[i][j]来说,i指的是前i件物品,j指的是还剩下多少背包空间。于是对于dp[i][j]来说,有公式:
在这里插入图片描述

import numpy as np
 
weight=[2,2,6,5,4]
value=[3,6,5,4,6]
weight_most=10
def bag_0_1(weight,value,weight_most):#return max value
    num = len(weight)
    weight.insert(0,0)#前0件要用
    value.insert(0,0)#前0件要用
    bag=np.zeros((num+1,weight_most+1),dtype=np.int32)#下标从零开始
    for i in range(1,num+1):
        for j in range(1,weight_most+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]
    # print(bag)
    return bag[-1,-1]
 
result=bag_0_1(weight,value,weight_most)
print(result)

空间复杂度的极致优化

时间复杂度:O(物品个数*背包大小)
空间复杂度:O(背包大小)
在这里插入图片描述

java代码:

public class KnapSack01 {
    public static int knapSack(int[] w, int[] v, int C) {
        int size = w.length;
        if (size == 0) {
            return 0;
        }

        int[] dp = new int[C + 1];
        //初始化第一行
        //仅考虑容量为C的背包放第0个物品的情况
        for (int i = 0; i <= C; i++) {
            dp[i] = w[0] <= i ? v[0] : 0;
        }
		//考虑容量为C的背包放第i~size-1个物品的情况,假设前i个已经计算过
        for (int i = 1; i < size; i++) {
            for (int j = C; j >= w[i]; j--) {
            	//容量为j的时候价值为dp[j](不放w[i]和放w[i]的最大值)
                dp[j] = Math.max(dp[j], v[i] + dp[j - w[i]]);
            }
        }
        return dp[C];
    }

    public static void main(String[] args) {
        int[] w = {2, 1, 3, 2};
        int[] v = {12, 10, 20, 15};
        System.out.println(knapSack(w, v, 5));
    }
}


1.4 变种问题

三维0-1背包

力扣474. 一和零
在这里插入图片描述
方法1:

class Solution:
    def findMaxForm(self, strs: List[str], m: int, n: int) -> int:
        L=len(strs)
        dp=[[[0]*(n+1) for _ in range(m+1)] for _ in range(L+1)]
        for i in range(1,L+1):
            count1,count2=strs[i-1].count("0"),strs[i-1].count("1")
            for j in range(m+1):
                for k in range(n+1):
                    dp[i][j][k]=dp[i-1][j][k]
                    if count1<=j and count2<=k:
                        dp[i][j][k]=max(dp[i][j][k],dp[i-1][j-count1][k-count2]+1)
        return dp[-1][-1][-1]

https://leetcode-cn.com/problems/ones-and-zeroes/solution/yi-he-ling-by-leetcode-solution-u2z2/
在这里插入图片描述
方法2:优化为二维数组

class Solution:
    def findMaxForm(self, strs: List[str], m: int, n: int) -> int:
        L=len(strs)
        dp=[[0]*(n+1) for _ in range(m+1)]
        count1,count2=strs[0].count("0"),strs[0].count("1")
        for j in range(m+1):
            for k in range(n+1):
                if count1<=j and count2<=k:
                    dp[j][k]=1
        for i in range(1,L):
            count1,count2=strs[i].count("0"),strs[i].count("1")
            for j in range(m,count1-1,-1):
                for k in range(n,count2-1,-1):
                    dp[j][k]=max(dp[j][k],dp[j-count1][k-count2]+1)
        return dp[m][n]

在这里插入图片描述

2. 完全背包

2.1 相关链接

动态规划:完全背包、多重背包
【Algorithm】完全背包 巧用公式变形法转化为01背包问题
leetcode 完全背包汇总贴 (更新ing)

2.2 问题描述

有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

2.3 解决实现

1)初始的方式-三重循环:

根据第i种物品放多少件进行决策,所以状态转移方程为
在这里插入图片描述
其中F[i-1][j-KC[i]]+KW[i]表示前i-1种物品中选取若干件物品放入剩余空间为j-K*C[i]的背包中所能得到的最大价值加上k件第i种物品;

设物品种数为N,背包容量为V,第i种物品体积为C[i],第i种物品价值为W[i]。

与01背包相同,完全背包也需要求出NV个状态F[i][j]。但是完全背包求F[i][j]时需要对k分别取0,…,j/C[i]求最大F[i][j]值。

这样代码应该是三层循环(物品数量,物品种类,背包大小这三个循环),伪代码如下:

F[0][]{0}  
F[][0]{0}  
for i←1 to N  
    do for j←1 to V  
        do for k←0 to j/C[i]  
           if(j >= k*C[i])  
                then F[i][k]max(F[i][k],F[i-1][j-k*C[i]]+k*W[i])  
return F[N][V]

很明显,这样一般情况下会超时,需要转化成时间复杂度比较低的在进行求解
经过思考可以想到完全背包可以转化为01背包:
2)转为01背包-二重循环:
因为同种物品可以多次选取,那么第i种物品最多可以选取V/C[i]件价值不变的物品,然后就转化为01背包问题。如果把第i种物品拆成体积为C[i]×2k价值W[i]×2k的物品,其中满足C[i]×2k≤V。即设F[i][j]表示出在前i种物品中选取若干件物品放入容量为j的背包所得的最大价值。那么对于第i种物品的出现,我们对第i种物品放不放入背包进行决策。如果不放那么F[i][j]=F[i-1][j];如果确定放,背包中应该出现至少一件第i种物品,所以F[i][j]种至少应该出现一件第i种物品,即F[i][j]=F[i][j-C[i]]+W[i]。为什么会是F[i][j-C[i]]+W[i]?因为F[i][j-C[i]]里面可能有第i种物品,也可能没有第i种物品。我们要确保F[i][j]至少有一件第i件物品,所以要预留C[i]的空间来存放一件第i种物品。
在这里插入图片描述

状态方程为:
在这里插入图片描述
0-1背包和完全背包的不同:

从二维数组上区别0-1背包和完全背包也就是状态转移方程就差别在放第i中物品时,完全背包在选择放这个物品时,最优解是F[i][j-c[i]]+w[i]即画表格中同行的那一个,而0-1背包比较的是F[i-1][j-c[i]]+w[i],上一行的那一个。

从一维数组上区别0-1背包和完全背包差别就在循环顺序上,0-1背包必须逆序,因为这样保证了不会重复选择已经选择的物品,而完全背包是顺序,顺序会覆盖以前的状态,所以存在选择多次的情况,也符合完全背包的题意。状态转移方程都为F[i] = max(F[i],dp[F-c[i]]+v[i])。
  
3)用一位数组进行空间优化:

#include<cstdio>
#include<algorithm>
using namespace std;
int w[300],c[300],f[300010];
int V,n;
int main()
{
    scanf("%d%d",&V,&n);
    for(int i=1; i<=n; i++)
    {
        scanf("%d%d",&w[i],&c[i]);
    }
    for(int i=1; i<=n; i++)
        for(int j=w[i]; j<=V; j++)//注意此处,与0-1背包不同,这里为顺序,0-1背包为逆序
            f[j]=max(f[j],f[j-w[i]]+c[i]);
    printf("max=%d\n",f[V]);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值