类型二:0-1背包问题

102 阅读2分钟

一、动态规划

  • 本质:
  • 题目条件: 出现每次只能向下或者向右移动一步

二、思路

  • 关键词:

  • 难点在于:

    • 定义dp[i][j] 一般要求什么就是把dp[i][j]定义为啥
    • 寻找转换关系 即当前dp[i][j]的结果取自哪里?在路径问题中,一般看到其移动路径,即可确定状态转移方程。比如“只能向下或者向右移动”,则说明dp[i][j]来自dp[i-1][j]或者dp[i][j-1]
    • 确定初始值

三、题目汇总

四、题解汇总

  • Code 416
解法一:使用回溯,超时
func canPartition(nums []int) bool {
    sum:=0
    for i:=0;i<len(nums);i++{
        sum=sum+nums[i]
    }
    sort.Ints(nums)
    target:=sum/2
    if sum%2!=0{
        return false
    }
    return dfs(nums,0,0,target)
}

func dfs(nums []int, index int,sum int,target int)bool{
    if sum==target{
        return true
    }
    if index>=len(nums) || sum>target{
        return false
    }
    for i:=index;i<len(nums);i++{
        if dfs(nums,i+1,sum+nums[i],target){
            return true
        }
    }
    return false
}

解法二:动态规划
func canPartition(nums []int) bool {
    // dp[i][j]表示从下标范围[0,i]内选取若干个元素,是否存在一种方案使得和为j
    // 变化的是:数组中的元素索引、数组元素的和
    // dp[i][j]=dp[i-1][j]& dp[i-1][j-nums[i]]
    // dp[i][0]=true dp[0][nums[0]=true
    if len(nums)<2{
        return false
    }
    sum:=0
    max:=nums[0]
    for i:=0;i<len(nums);i++{
        sum=sum+nums[i]
        if nums[i]>max{
            max=nums[i]
        }
    }
    
    target:=sum/2
    if max>target{
        return false
    }
    if sum%2!=0{
        return false
    }
    
    dp:=make([][]bool,len(nums))
    for i:=0;i<len(nums);i++{
        dp[i]=make([]bool,target+1)
    }
    dp[0][0]=true
    dp[0][nums[0]]=true
    for i:=1;i<len(nums);i++{
        for j:=0;j<=target;j++{
            //分两种:选取i或者不选取i
            if j>=nums[i]{
                dp[i][j]=dp[i-1][j]||dp[i-1][j-nums[i]]
            }else{ //无法选取i
                dp[i][j]=dp[i-1][j]
            }
        }
    } 
    return dp[len(nums)-1][target]
}