此题为0-1背包问题的变体。
第一版是背包问题的常规思路,方法是可行的,但是会Memory Limit Exceeded。
第二版是常规思路的改进版,巧妙地将矩阵的元素类型从int改为boolean,以降低对空间的要求。
Version 1 Memory Limit Exceeded
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) {
// 2015-05-21 01背包
if (A == null || A.length == 0 || m == 0) {
return 0;
}
int n = A.length;
int[][] dp = new int[n + 1][m + 1];
for (int j = 0; j <= m; j++) {
dp[0][j] = 0;
}
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= m; j++) {
dp[i][j] = dp[i - 1][j]; //dont put
if (A[i - 1] <= j) { // 有可能放下
dp[i][j] = Math.max(dp[i][j],
dp[i - 1][j - A[i - 1]] + A[i - 1]);
}
} // for
} // for
return dp[n][m];
}
}
Version 2
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) {
// 2015-05-21 01背包
// dp[i][j]表示只考虑前i个道具的情况下,背包内道具总大小能否达到j。
// dp[i][:]只考虑前i个道具的情况,每一种道具可以选也可以不选
// dp[:][j]只考虑背包大小是j的情况
if (A == null || A.length == 0 || m == 0) {
return 0;
}
int packSize = m; // 背包容量
int itemNum = A.length; // 道具个数
boolean[][] dp = new boolean[itemNum + 1][packSize + 1];
dp[0][0] = true;
// 计算dp[i][j] 从第二行开始
// 需要参考两个数dp[i - 1][j] dp[i - 1][j - 当前讨论的道具大小]
for (int i = 1; i <= itemNum; i++) { // 选择某一行
for (int j = 0; j <= packSize; j++) { // 从左到右
dp[i][j] = dp[i - 1][j]; //不放第一i个道具,直接取上边的数
if (A[i - 1] <= j && dp[i - 1][j - A[i - 1]]) { //可以放下第i个道具
// A[i - 1]是我们现在讨论的第i个道具的大小,j表示背包总容量
dp[i][j] = true;
}
} // for
} // for
// 找最佳 最后一行 从右到左 找第一个true
for (int j = packSize; j >= 0; j--) {
if(dp[itemNum][j]) {
return j;
}
}
return 0;
}
}
/**
* 举例 4 items with size [2, 3, 5, 7], the backpack size is 11,return 10
* 0 1 2 3 4 5 6 7 8 9 0 1
* 0 t f f f f f f f f f f f
* 1 t f t f f f f f f f f f
* 2 t f t t f t f f f f f f
* 3 t f t t f t f t t f t f
* 4 t f t t f t f t t t t f
*/
/**
* 学习dp的关键点
* 搞清楚dp[i][j]的含义
* 搞清楚dp[i][j]依赖那些元素
* 一个测试用例和对应的矩阵
*/