题目地址:
https://www.lintcode.com/problem/backpack-v/description
给定一个数组 A A A,代表每个物品的体积。给定一个容积 m m m,问塞满这个容积的物品组合方案数是多少。每个物品只能选一个。
思路是动态规划。设 f [ i ] [ j ] f[i][j] f[i][j]是只在 A [ 0 : i ] A[0:i] A[0:i]里挑选,能塞满 m m m容积的组合方案数。那么可以根据挑不挑选 A [ i ] A[i] A[i]来分类,如果挑,那么方案数是 f [ i − 1 ] [ j − A [ i ] ] f[i-1][j-A[i]] f[i−1][j−A[i]],如果不挑,则方案数是 f [ i − 1 ] [ j ] f[i-1][j] f[i−1][j]。所以: f [ i ] [ j ] = f [ i − 1 ] [ j − A [ i ] ] + f [ i − 1 ] [ j ] f[i][j]=f[i-1][j-A[i]]+f[i-1][j] f[i][j]=f[i−1][j−A[i]]+f[i−1][j]边界条件为 f [ 0 ] [ k A [ 0 ] ] = 1 , k = 0 , 1 f[0][kA[0]]=1,k=0,1 f[0][kA[0]]=1,k=0,1代码如下:
public class Solution {
/**
* @param nums: an integer array and all positive numbers
* @param target: An integer
* @return: An integer
*/
public int backPackV(int[] nums, int target) {
// write your code here
if (nums == null || nums.length == 0) {
return 0;
}
int[][] dp = new int[nums.length][target + 1];
for (int i = 0; i < 2; i++) {
if (i * nums[0] <= target) {
dp[0][i * nums[0]] = 1;
}
}
for (int i = 1; i < nums.length; i++) {
for (int j = 0; j <= target; j++) {
dp[i][j] = dp[i - 1][j];
if (j >= nums[i]) {
dp[i][j] += dp[i - 1][j - nums[i]];
}
}
}
return dp[nums.length - 1][target];
}
}
时空复杂度 O ( l A m ) O(l_Am) O(lAm)。
考虑空间优化。由于 f [ i ] [ j ] f[i][j] f[i][j]只取决于上面一行的值,所以可以考虑更新的时候直接覆盖原数组。这里需要注意,每一行需要从右向左更新,因为 f [ i ] [ j ] f[i][j] f[i][j]要依赖 f [ i − 1 ] [ j − A [ i ] ] f[i-1][j-A[i]] f[i−1][j−A[i]]。代码如下:
public class Solution {
/**
* @param nums: an integer array and all positive numbers
* @param target: An integer
* @return: An integer
*/
public int backPackV(int[] nums, int target) {
// write your code here
if (nums == null || nums.length == 0) {
return 0;
}
int[] dp = new int[target + 1];
for (int i = 0; i < 2; i++) {
if (i * nums[0] <= target) {
dp[i * nums[0]] = 1;
}
}
for (int i = 1; i < nums.length; i++) {
// 从右向左更新
for (int j = target; j >= 0; j--) {
if (j >= nums[i]) {
dp[j] += dp[j - nums[i]];
}
}
}
return dp[target];
}
}
时间复杂度不变,空间 O ( m ) O(m) O(m)。