算法整理——【动态规划练习(5)完全背包进阶】

在上一篇博客算法整理——【动态规划练习(4)完全背包】-CSDN博客的基础上,继续补充完全背包相关问题。

一、完全背包中的排列和组合

在纯完全背包问题中,我们说先遍历物品后遍历背包和先遍历背包后遍历物品都是可以的,因为返回的是值的最大和。但如果要返回的是背包里的物品或其种类数,则涉及到排列还是组合的问题,如{1,2}和{2,1}算一个还是两个。

现在我们来看一个组合题518. 零钱兑换 II - 力扣(LeetCode)和一个排列题377. 组合总和 Ⅳ - 力扣(LeetCode)

排列还是组合,代码中主要由循环的顺序决定。如果是外层遍历物品,内层遍历背包,则物品1永远会在2前面,不会存在{2,1},是组合。如果外层遍历背包,内层遍历物品,则背包的每一个值都会考虑1或2,会存在{2,1},是排列。

总结:如果求组合数就是外层for循环遍历物品,内层for遍历背包。如果求排列数就是外层for遍历背包,内层for循环遍历物品。

组合题代码如下:

class Solution {
public:
    int change(int amount, vector<int>& coins) {
        vector<int> dp(amount+1,0);
        dp[0] = 1;
        for(int i = 0; i<coins.size(); i++)
        {
            for(int j = coins[i]; j<=amount; j++)
            {
                dp[j] += dp[j-coins[i]];
            }
        }
        return dp[amount];
    }
};

排列题代码如下:

class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        vector<int> dp(target+1,0);
        dp[0] = 1;
        for(int i = 1; i<=target; i++)
        {
            for(int j = 0; j<nums.size(); j++)
            {
                if(i - nums[j] >= 0 && dp[i] < INT_MAX - dp[i - nums[j]]) dp[i] += dp[i-nums[j]];
            }
        }
        return dp[target];
    }
};

二、单词拆分

题目为139. 单词拆分 - 力扣(LeetCode),给你一个字符串 s 和一个字符串列表 wordDict 作为字典。如果可以利用字典中出现的一个或多个单词拼接出 s 则返回 true

本题和回溯算法中分割回文串的题目相似,本题也可以用回溯方法枚举分割情况。普通的回溯可能会超时,因为每个位置都有两个状态,我们可以通过增加memory数组保存之前递归后的结果,这样可以优化。

现在我们用动态规划的多重背包来解决这个问题,把字符串看成背包,单词看成物品,单词可以被重复使用,所以是一个完全背包。使用动规五部曲分析:

①确定dp数组和下标的含义。dp[i]的意思是,字符串取前面长度为i的部分,是否可以完全拆分为单词。

②递推公式。如果dp[j]为1且[j,i]这个区间的子串在字典里,那么dp[i]就是1。

③初始化。dp[0]=1,其他初始化为0。

④遍历顺序。本题其实为排列问题,因为字符串和单词都是有序的。所以我们要先遍历背包再遍历物品。

⑤尝试推导dp数组并检查。

完整代码如下:

class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        unordered_set<string> Dic(wordDict.begin(),wordDict.end());
        vector<bool> dp(s.size()+1,0);
        dp[0] = 1;
        for(int i = 1; i<=s.size(); i++)
        {
            for(int j = 0; j<i; j++)
            {
                string word = s.substr(j,i-j);//substr(起始位置,截取的个数)
                if(Dic.find(word) != Dic.end() && dp[j])
                {
                    dp[i] = 1;
                }
            }
        }
        return dp[s.size()];
    }
};

说明:本文为作者整理知识点用于复习巩固,参考了代码随想录的讲解,有问题可以联系作者欢迎讨论~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值