【两次过】Lintcode 92. 背包问题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/majichen95/article/details/89052002

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

样例

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

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

挑战

O(n x m) time and O(m) memory.

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

注意事项

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


解题思路:

典型的背包问题。设横坐标i为前i个物品(包含i),纵坐标为背包容量,中间每个坐标值dp[i][j]表示将前i个物品挑选若干放入容量为j的背包,所装的最大值

边界条件:第一排,若背包容量 >= 第一个物品的大小,则可以装入,否则为0

递推式:两种情况:

(1)不装入当前物品,直接为上一个物品的结果:dp[i][j] = dp[i-1][j]

(2)若能装入当前物品,则先装入当前物品,然后加上剩余容量能装下的物品:dp[i][j] = A[i]+dp[i-1][j-A[i]]

最后取这两种情况的最大值为当前dp[i][j]的结果

时间复杂度:O(m*n),空间复杂度:O(m*n),m代表背包容量,n代表物体数量

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) {
        // write your code here
        //dp[i][j]表示将前i个物品挑选若干放入容量为j的背包,所装的最大值
        //其中i属于[0,A.length),0表示第一个物品,j属于[0,m],0表示容量为0
        int[][] dp = new int[A.length][m+1];
        
        //边界条件,第一行
        for(int j=0; j<=m; j++)
            dp[0][j] = ( j >= A[0] ? A[0] : 0);
        
        //递推式
        for(int i=1; i<A.length; i++){
            for(int j=0; j<=m; j++){
                dp[i][j] = dp[i-1][j];
                if(j >= A[i])
                    dp[i][j] = Math.max(dp[i][j], A[i]+dp[i-1][j-A[i]]);
            }
        }
        
        return dp[A.length-1][m];
    }
}

空间优化1:每一次递归第i行元素都只依赖于第i-1行元素,也就是说只需要2行元素即可求解。而i与i-1恰好一个是奇数一个是偶数,所以可以通过奇偶交换来达到目的,对所有dp[i][j]变为dp[i%2][j]

此时空间复杂度为O(2*m),m为背包容量

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) {
        // write your code here
        int[][] dp = new int[2][m+1];
        
        //边界条件,第一行
        for(int j=0; j<=m; j++)
            dp[0][j] = ( j >= A[0] ? A[0] : 0);
        
        //递推式
        for(int i=1; i<A.length; i++){
            for(int j=0; j<=m; j++){
                dp[i%2][j] = dp[(i-1)%2][j];
                if(j >= A[i])
                    dp[i%2][j] = Math.max(dp[i%2][j], A[i]+dp[(i-1)%2][j-A[i]]);
            }
        }
        
        return dp[(A.length-1)%2][m];
    }
}

空间优化2:我们可以看出每次求dp[i][j]只需要它上面的元素和左上的元素,所以我们还可以精简右上的元素,将二维数组变为一维数组。此时只需要从右向左进行计算,比较当前元素以及左边元素即可,同时向左进行到背包无法装下当前物品为止,因为此时就等于当前元素本身,这样还节省了时间

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) {
        // write your code here
        int[] dp = new int[m+1];

        for(int j=0; j<=m; j++)
            dp[j] = (j >= A[0]) ? A[0] : 0;

        for(int i=1; i<A.length; i++)
            for(int j=m; j>=A[i]; j--)
                dp[j] = Math.max(dp[j], dp[j - A[i]] + A[i]);
        
        return dp[m];
    }
}

猜你喜欢

转载自blog.csdn.net/majichen95/article/details/89052002