动态规划:背包问题详细讲解和讨论

1. 动态规划原理

动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。

例如背包问题,他可以通过递归求解。假设给定 N N N个物品列表重量分别为
{ W 1 , W 2 , W 3 , W 4 , . . . , W N } \{ {W_1},{W_2},{W_3},{W_4},...,{W_N}\} { W1,W2,W3,W4,...,WN},给定背包容量为 V V V,列举背包可以放入物品的所有情形。这里假定每个物品只有1个。
利用递归的思想可以这么求解:
首先任取 N N N个物品中的一个物品,其重量为 W i {W_i} Wi,若 W i < V {W_i} < V Wi<V,则可以将物品 i i i放入背包,同时对剩下 N − 1 N-1 N1个物品,通过递归求解其是否能放入体积为 V − W i V-{W_i} VWi的背包。
事实上,进行递归求解时会存在很多重复计算。假设 W i , W j {W_i},{W_j} Wi,Wj以及 W i + W j {W_i}+{W_j} Wi+Wj均小于 V V V。很显然递归求解容积为 W i + W j {W_i}+{W_j} Wi+Wj解时会重复计算一次容积分别为 W i , W j {W_i},{W_j} Wi,Wj的解。这就存在着很多重复计算。
而用动态规划思想去解决背包问题,原理就是将容积从小变大,依次求解 V = { 1 , 2 , 3 , 4 , . . . , V } V=\{ {1},{2},{3},{4},...,{V}\} V={ 1,2,3,4,...,V}的所有解。当存在容积 V i {V_i} Vi可以分解成多个子容积时,其解也与子问题的解有对应关系,可以直接将之前计算的结果应用于当前容积,以避免重复计算。

2.实例讲解

Leetcode第39题为例,分别介绍递归法与动态规划法如何实现。题目介绍如下:

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的数字可以无限制重复被选取。

说明:

所有数字(包括 target)都是正整数。
解集不能包含重复的组合。
示例 1:

输入: candidates = [2,3,6,7], target = 7,
所求解集为:
[
[7],
[2,2,3]
]
示例 2:

输入: candidates = [2,3,5], target = 8,
所求解集为:
[
[2,2,2,2],
[2,3,3],
[3,5]
]

递归法的思路比较简单,以 c a n d i d a t e s = [ 2 , 3 , 6 , 7 ] , t a r g e t = 7 candidates = [2,3,6,7], target = 7 candidates=[2,3,6,7],target=7为例,首先 7 7 7在数组 c a n d i d a t e s = [ 2 , 3 , 6 , 7 ] candidates = [2,3,6,7] candidates=[2,3,6,7]中,因此 [ 7 ] [7] [7]是满足条件的一个解。由于数组有4个元素,用 7 7 7减去每个元素可以得到一个新 t a r g e t target target集合 t a r g e t n e w = { 5 , 4 , 1 , 0 } {target_{new}} =\{5,4,1,0\} targetnew={ 5,4,1,0},依次求出满足 t a r g e t n e w = { 5 , 4 , 1 , 0 } {target_{new}} =\{5,4,1,0\} targetnew={ 5,4,1,0}的解,加上上一次减去的数组中的元素,可以得到满足条件的新解。同时观察到 c a n d i d a t e s candidates candidates中最小元素不小于 2 2 2,因此 t a r g e t n e w = { 1 , 0 } {target_{new}} =\{1,0\} targetnew={ 1,0}的解是不存在的。通过这种判断可以减少循环数量。

实现代码如下:

vector<vector<int>> combinationSumSort(vector<int>& candidates, int target) {
   
    vector<vector<int>> result;
    size_t size = candidates.size();
    if (0 == size)
        return result;
    if (target < candidates[0])
        return result;
    if (find(candidates.begin(), candidates.end(), target) != candidates.end())
    {
   
        vector<int> buf(1, target);
        result.push_back(buf);
    }
    for (int i = 0; i < size; ++i)
    {
   
        int subTarget = target - candidates[i];
        if (subTarget >= candidates[0])
        {
   
            vector<vector<int>> lastResult = combinationSum(candidates, subTarget);
            for (auto& buffer : lastResult)
            {
   
                buffer.push_back(candidates[i]);
                sort(buffer.begin(), buffer.end());
                if (find(result.begin(), result.end(), buffer) == result.end())
                    result.push_back(buffer);
            }
        }
    }
    return result;
}

vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
   
    sort(candidates.begin(), candidates.end());
    return combinationSumSort(candidates, target);
}

需要注意的是:由于需要将 t a r g e t target target与数组中第一个元素判断大小,进行快速判断是否存在满足当前 t a r g e t target target的解。因此需要先对数组 c a n d i d a t e s candidates candidates进行排序。

递归求解时,存在很多重复计算。而动态规划可以减少重复计算。利用动态规划实现这个问题求解的思路如下:

已知最终求解数组满足 t a r g e t = 7 target=7 target=7的解,我们可以依次求出满足 t a r g e t = { 1 , 2 , 3 , 4 , 5 , 6 , 7

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值