动态规划-背包问题(二)

该题是lintcode上的125 · 背包问题(二)算法题,该题的解题思路是按照九章侯老师给的方法去实现的。

相关的题目:

动态规划-背包问题
动态规划-背包问题(二)
动态规划-背包问题(三)
动态规划-Backpack VI
动态规划-背包问题 V

1 描述

有 n 个物品和一个大小为 m 的背包. 给定数组 A 表示每个物品的大小和数组 V 表示每个物品的价值.

问最多能装入背包的总价值是多大?

1. A[i]V[i],n,m均为整数
2. 您不能将物品进行切分
3. 您所挑选的要装入背包的物品的总大小不能超过m
4. 每个物品只能取一次
5. m <= 100,即len(a)len(v) <= 100

2 样例

2.1 样例 1:

输入:

m = 10
A = [2, 3, 5, 7]
V = [1, 5, 2, 4]
输出:

9
解释:

装入 A[1] 和 A[3] 可以得到最大价值, V[1] + V[3] = 9

2.2 样例 2:

输入:

m = 10
A = [2, 3, 8]
V = [2, 5, 8]
输出:

10
解释:

装入 A[0] 和 A[2] 可以得到最大价值, V[0] + V[2] = 10

2.3 挑战

O(nm) 空间复杂度可以通过, 你能把空间复杂度优化为O(m)吗?

3 算法解题思路以及实现方法

3.1 算法解题思路

3.1.1 确定状态

该问题主要是要知道N个物品是否可以拼出重量M,对于每个重量M,最大总价值为多少。
最后一步:最后一个物品是否需要放入背包中
选择1:如果前N - 1个物品就可以拼出重量M,最大价值为V,那么前N个物品也能拼出M且总价值为M。
选择2:如果前N - 1个物品能拼出M - An-1,最大总价值为V,则再加上最后一个物品(重An-1,价值为Vn-1),能拼出M,总价值为:V + Vn-1

设:f[i][]j表示用前i个物品拼出重量为j时的最大总价值(-1表示不能拼出j)

3.1.2 转移方程

在这里插入图片描述

3.1.3 初始条件和边界情况

  • 设:f[i][]j表示用前i个物品拼出重量为j时的最大总价值(-1表示不能拼出j)
  • f[i][w] = max{f[i-1][w], f[i-1][w-Ai-1] + Vi-1 | w≥Ai-1 且f[i-1][w-Ai-1] ≠-1}
  • 初始条件:
    • f[0][0] = 0,表示0个物品可以拼出重量为0,最大总价值为0
    • f[0][1…M] = -1,表示0个物品不能拼出重量大于0的重量
  • 边界情况:
    • f[i - 1][j - Ai-1]只能在j >= Ai-1,并且f[i - 1][j - Ai-1] != -1时使用

3.1.4 计算顺序

  • 初始化f[0][0], f[0][1], …, f[0][M]

  • 计算前1个物品可以拼出各种重量的最大总价值:f[1][0], f[1][1], …, f[1][M]

  • 计算前N个物品拼出各种重量的最大价值:f[N][0], f[N][2], …, f[N][M]

  • 最后结果为:max0<=j<=M{f[N][j] | f[N][j] ≠-1}

  • 时间复杂度为:O(MN)

  • 空间复杂度为:O(MN)

  • 空间优化后的复杂度为:O(M)

3.2 空间复杂度为O(MN)的算法实现

3.2.1 java实现

public class Solution {
    /**
     * @param m: An integer m denotes the size of a backpack
     * @param a: Given n items with size A[i]
     * @param v: Given n items with value V[i]
     * @return: The maximum value
     */
    public int backPackII(int m, int[] a, int[] v) {
        // write your code here
        int n = a.length;
        int [][] f = new int[n + 1][m + 1];

        f[0][0] = 0;
        for (int i = 1; i <= m; i++) {
            f[0][i] = -1;
        }

        for (int i = 1; i <= n; i++) {
            for (int j = 0; j <= m; j++) {
                f[i][j] = f[i - 1][j];
                if (j >= a[i - 1] && f[i - 1][j - a[i - 1]] != -1) {
                    f[i][j] = Math.max(f[i][j], f[i - 1][j - a[i - 1]] + v[i - 1]);
                }
            }
        }

        int res = 0;
        for (int i = 0; i <= m; i++) {
            if (f[n][i] != -1) {
                res = Math.max(res, f[n][i]);
            }
        }

        return res;
    }
}

3.2.2 C++实现

class Solution {
public:
    /**
     * @param m: An integer m denotes the size of a backpack
     * @param a: Given n items with size A[i]
     * @param v: Given n items with value V[i]
     * @return: The maximum value
     */
    int backPackII(int m, vector<int> &a, vector<int> &v) {
        // write your code here
        int n = a.size();
        vector<vector<int>> f(n + 1, vector<int>(m + 1, -1));

        f[0][0] = 0;        
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j <= m; j++) {
                f[i][j] = f[i - 1][j];
                if ((j >= a[i - 1]) && (f[i - 1][j - a[i - 1]] != -1)) {
                    f[i][j] = max(f[i][j], f[i - 1][j - a[i - 1]] + v[i - 1]);
                }
            }
        }

        int res = 0;
        for (int i = 0; i <= m; i++) {
            if (f[n][i] != -1) {
                res = max(res, f[n][i]);
            }
        }

        return res;
    }
};

3.3 空间优化后的算法实现

class Solution {
public:
    /**
     * @param m: An integer m denotes the size of a backpack
     * @param a: Given n items with size A[i]
     * @param v: Given n items with value V[i]
     * @return: The maximum value
     */
    int backPackII(int m, vector<int> &a, vector<int> &v) {
        // write your code here
        int n = a.size();
        vector<int> f(m + 1, 0);

        for (int i = 1; i <= n; i++) {
            for (int j = m; j >= a[i - 1]; j--) {
                f[j] = max(f[j], f[j - a[i - 1]] + v[i - 1]);
            }
        }

        return f[m];
    }
};

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值