背包问题之完全背包

概述

完全背包问题是指不限制物品使用次数的背包问题
虽然物品的使用次数为无限,但是背包容量仍有限,所以实际上每个物品最多取m/A[i]件。

因此,完全背包问题可以按照每个物品最多可取的数量转化为0-1背包问题,即如果物品a最多可取x件,则将物品a改为x个物品a,再使用0-1背包的思路解决。

其实,完全背包问题可以通过将0-1背包的倒序枚举转变为正序枚举完成,从而达到多次选择同一个物品的目的。

322. 零钱兑换

题目

给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/coin-change
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解法

public int coinChange(int[] coins, int amount) {
    int[] dp = new int[amount + 1];
    int max = amount + 1;
    Arrays.fill(dp,max);
    dp[0] = 0;
    for (int j = 1; j <= amount; j++) {
        for(int i = 0;i < coins.length;i++) {
            if(j >= coins[i])
                dp[j] = Math.min(dp[j],dp[j - coins[i]] + 1);//dp数组中存的是最少硬币个数
        }
    }

    return dp[amount] > amount ? -1 : dp[amount];
}

139. 单词拆分

题目

给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。

说明:

拆分时可以重复使用字典中的单词。
你可以假设字典中没有重复的单词。
示例 1:

输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true
解释: 返回 true 因为 "leetcode" 可以被拆分成 "leet code"。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/word-break
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

代码

dict 中的单词没有使用次数的限制,因此这是一个完全背包问题。

public boolean wordBreak(String s, List<String> wordDict) {
    boolean[] dp = new boolean[s.length() + 1];
    dp[0] = true;
    for (int i = 1; i <= s.length(); i++) {
        for(String word : wordDict)//求解有顺序的(目标字符串中单词的排列是有顺序的)完全背包问题时,对物品的迭代应该放在最里层,对背包的迭代放在外层,只有这样才能让物品按一定顺序放入背包中。
            if(i >= word.length())
                if (word.equals(s.substring(i - word.length(), i))) {
                    dp[i] = dp[i] || dp[i - word.length()];
                }
    }

    return dp[s.length()];
}

本题说明,对于一些题目,先迭代物品还是迭代背包都可以,而有一些则需要具体分析,比如本题这种有顺序要求的,而一般思考时,无特定要求的情况下,可以先考虑先迭代物品,再迭代背包

原文地址:https://www.cnblogs.com/wunsiang/p/12765089.html