算法分享系列No.14--DP-- 0/1背包问题

0-1背包逐步推导过程,可以详见:0-1背包问题详解(一步一步超详细) - 百度文库

1、LeetCode相关问题涉及:

322. 零钱兑换  中等 --出现频率67.34%

416. 分割等和子集 中等 -- 出现频率71.6%

2、动态规划--DP

        维基百科的描述:“动态规划( Dynamic Programming, DP)在查找有很多重叠子问题的情况的最优解时有效。它将问题重新组合成子问题。为了避免多次解决这些子问题,它们的结果都逐渐被计算并被保存,从简单的问题直到整个问题都被解决。
        动态规划只能应用于有【最优子结构】的问题。
        动态规划和其它遍历算法(如深/ 广度优先搜索)都是将原问题拆成多个子问题然后求解,他们之间最本质的区别是,动态规划 保存子问题的解 避免重复计算 。解决动态规划问题的 关键是找到状态转移方程 ,这样我们可以通过计算和储存子问题的解来求解最终问题。

3、LeetCode416--分割等和子集

算法分享系列No.14--DP-- 0/1背包问题_第1张图片

 3-1、 该题如何转移为0-1背包问题

1)目标分割成两个等和子集,既该数组的总和sum/2 可以转换为背包的总容量,因此此题可以转换为两个背包问题,并且要求最后每个背包的容量为sum / 2.

2)由于是等和分割,因此数组总和sum 必须是偶数

3)由于每个数组元素,都要分配到两个背包的其中一个去,因此该数组的最大值,即Max(nums[i]) 必不能大于sum /2 , 否则Max(nums[i])是无法分配到背包中的。

4)这道题不需要考虑价值,因此我们只需要通过一个布尔值矩阵来表示状态转移 矩阵。注意边界条件的处理。

  • 因此创建二维数组dp(n行:nums的个数, target+1 列:背包容量sum / 2, 因此要循环到sum / 2 +1 ),dp[i][j] 表示从数组的 [0,i]下标范围内选取若干个正整数(可以是 0 个),是否存在一种选取方案使得被选取的正整数的和等于 j。初始时,dp 中的全部元素都是 false。

注意以下边界情况:

  • 如果不选取任何正整数,则被选取的正整数等于 0。因此对于所有 0 ≤ i < n,都有 dp[i][0]=true。
  • 当 i==0 时,只有一个正整数 nums[0] 可以被选取,因此 dp[0][nums[0]]=true。

 递推公式(j为背包容量):            
               if(nums[i] < j){
                  //此时容量充足,可以选择,也可以不选择
                  dp[i][j] = dp[i-1][j] || dp[i-1][j-nums[i]];
               }else{
                  //此时背包容量不够,无法选择
                  dp[i][j]  = dp[i-1][j];
               }

class Solution {
    public boolean canPartition(int[] nums) {
        int sum = 0;
        int max = 0;
        int len = nums.length;
        for(int i = 0; i< len; i++){
           sum += nums[i];
           max = Math.max(max, nums[i]);
        }
        //汇总和一定是偶数
        if(sum % 2 != 0)
             return false;
        int target = sum /2;
        //最大值一定比目标背包(sum /2 )小,否则无法分割成等和子集
        if(max > target)
             return false;
        
        boolean[][] dp = new boolean[len][target+1];
        // 初始第0行,此时目标值nums[0]比目标值小,则为true
        if(nums[0]

动态规划:
时间复杂度:O(n×target), n 是数组的长度,target 是整个数组的元素和的一半
空间复杂度:O(n×target) , 空间复杂度取决于dp 数组, dp数组可以优化成一维数组。

空间复杂度优化,dp二维数组可以优化成一维数组,空间复杂度为O(n), 优化代码如下:

        boolean[] dp = new boolean[target + 1];
        dp[0] = true;
        for (int i = 0; i < n; i++) {
            int num = nums[i];
            for (int j = target; j >= num; --j) {
                dp[j] |= dp[j - num];
            }
        }


 

你可能感兴趣的:(算法,动态规划,面试,java,udp)