「LintCode」92. 背包问题(动态规划)

n 个物品中挑选若干物品装入背包,最多能装多满?假设背包的大小为 m,每个物品的大小为 A[i]

样例:

样例 1:
	输入:  [3,4,8,5], backpack size=10
	输出:  9

样例 2:
	输入:  [2,3,5,7], backpack size=12
	输出:  12	

挑战:

O ( n × m ) O(n \times m) time and O ( m ) O(m) memory.

O ( n × m O(n \times m ) memory is also acceptable if you do not know how to optimize memory.

注意事项

你不可以将物品进行切割。

分析:最原始的背包问题。每个物品有「选」与「不选」两种选择。

注意:初始化的时候,只要是容量大于等于 A[0] 的容量,都应该把下标为 0 的这件物品选进去。

Java 代码:

public class Solution {

    /**
     * @param m: An integer m denotes the size of a backpack
     * @param A: Given n items with size A[i]
     * @return: The maximum size
     */
    public int backPack(int m, int[] A) {
        int len = A.length;
        int[][] dp = new int[len][m + 1];

        // 初始化要小心
        for (int j = 0; j <= m ; j++) {
            if (A[0] <= j) {
                dp[0][j] = A[0];
            }
        }

        for (int i = 1; i < len; i++) {
            for (int j = 0; j <= m; j++) {
                // 不选 A[i]
                dp[i][j] = dp[i - 1][j];
                if (A[i] <= j) {
                    // 选 A[i]
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - A[i]] + A[i]);
                }
            }
        }
        return dp[len - 1][m];
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
//        int m = 10;
//        int[] A = {3, 4, 8, 5};

//        int m = 12;
//        int[] A = {2, 3, 5, 7};

        int m = 90;
        int[] A = {12, 3, 7, 4, 5, 13, 2, 8, 4, 7, 6, 5, 7};
        int res = solution.backPack(m, A);
        System.out.println(res);
    }
}

暂时不优化空间。先保证代码好懂。

技巧 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]
     * @return: The maximum size
     */
    public int backPack(int m, int[] A) {
        int len = A.length;
        // 多设置的这一行是「哨兵」
        // 可以认为是第 0 件物品的体积为 0
        // 这样就可以免去初始化的过程
        int[][] dp = new int[len + 1][m + 1];

        // 后面所有 dp 的第 1 个维度都要加 1
        for (int i = 0; i < len; i++) {
            for (int j = 0; j <= m; j++) {
                // 不选 A[i]
                dp[i + 1][j] = dp[i][j];
                if (A[i] <= j) {
                    // 选 A[i]
                    dp[i + 1][j] = Math.max(dp[i][j], dp[i][j - A[i]] + A[i]);
                }
            }
        }
        // 注意这里应该返回 dp[len][m]
        return dp[len][m];
    }
}

技巧 2:状态压缩

  • 由于只关心表格最后一行、最后一个状态的值;
  • 填写每一行只参考了它上一行的正上方,以及上一行左边的数值。

所以,可以只使用一行,倒过来填写。

想不到没有关系,这种知识点。只需要别人告诉我们,我们理解以后懂得运用即可。

Java 代码:

public class Solution {

    /**
     * @param m: An integer m denotes the size of a backpack
     * @param A: Given n items with size A[i]
     * @return: The maximum size
     */
    public int backPack(int m, int[] A) {
        // 状态压缩的代码,依然是加上了哨兵
        int len = A.length;
        int[] dp = new int[m + 1];

        for (int i = 0; i < len; i++) {
            // 注意:这样写,就省去了把上一行的值抄下来的过程,请读者体会

            // 注意这里要改成 j--
            for (int j = m; j >= 0; j--) {
                if (A[i] <= j) {
                    dp[j] = Math.max(dp[j], dp[j - A[i]] + A[i]);
                }
            }
        }

        return dp[m];
    }
}
发布了455 篇原创文章 · 获赞 348 · 访问量 126万+

猜你喜欢

转载自blog.csdn.net/lw_power/article/details/104941996