背包问题之完全背包
概述
完全背包问题是指不限制物品使用次数的背包问题
虽然物品的使用次数为无限,但是背包容量仍有限,所以实际上每个物品最多取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()];
}
本题说明,对于一些题目,先迭代物品还是迭代背包都可以,而有一些则需要具体分析,比如本题这种有顺序要求的,而一般思考时,无特定要求的情况下,可以先考虑先迭代物品,再迭代背包