知识总结
今天写了一道题目, 但是还挺难的, 而且是面试高频题目
还过了一遍多重背包问题.
多重背包与01背包的区别在于多重背包限制了物品的个数, 某些物品的个数可能不为1, 可以使用两次或者多次. 问题其实也可以转化成01背包.
代码参考如下
时间复杂度 O(m * n * k)
public void testMultiPack1(){
// 版本一:改变物品数量为01背包格式
List<Integer> weight = new ArrayList<>(Arrays.asList(1, 3, 4));
List<Integer> value = new ArrayList<>(Arrays.asList(15, 20, 30));
List<Integer> nums = new ArrayList<>(Arrays.asList(2, 3, 2));
int bagWeight = 10;
for (int i = 0; i < nums.size(); i++) {
while (nums.get(i) > 1) { // 把物品展开为i
weight.add(weight.get(i));
value.add(value.get(i));
nums.set(i, nums.get(i) - 1);
}
}
int[] dp = new int[bagWeight + 1];
for(int i = 0; i < weight.size(); i++) { // 遍历物品
for(int j = bagWeight; j >= weight.get(i); j--) { // 遍历背包容量
dp[j] = Math.max(dp[j], dp[j - weight.get(i)] + value.get(i));
}
System.out.println(Arrays.toString(dp));
}
}
public void testMultiPack2(){
// 版本二:改变遍历个数
int[] weight = new int[] {1, 3, 4};
int[] value = new int[] {15, 20, 30};
int[] nums = new int[] {2, 3, 2};
int bagWeight = 10;
int[] dp = new int[bagWeight + 1];
for(int i = 0; i < weight.length; i++) { // 遍历物品
for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量
// 以上为01背包,然后加一个遍历个数
for (int k = 1; k <= nums[i] && (j - k * weight[i]) >= 0; k++) { // 遍历个数
dp[j] = Math.max(dp[j], dp[j - k * weight[i]] + k * value[i]);
}
System.out.println(Arrays.toString(dp));
}
}
}
Leetcode 139. 单词拆分
题目链接
题目说明
给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s 。
注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。
代码说明
重点是找到递推公式以及如何遍历物品.
首先将问题转化成完全背包问题, 物品为每个单词, 问背包是否能由物品装满.
递推公式为
如果dp[j]可以由单词组成, 并且字符串[j, i]的子串属于worddic, 则dp[i]也可以由单词组成
dp[i] = dp[j] && worddict.contains(s.substring(j, i))
遍历顺序:
先背包再物品 : 排列数
先物品再背包 : 组合数
因为applepenapple != penappleapple, 所以本题需要用排列数, 遍历顺序为先背包容量再物品
时间复杂度为O(N ^ 3), 因为substring方法的复杂度为O(n)
class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
boolean[] dp = new boolean[s.length() + 1];
HashSet<String> set = new HashSet<>(wordDict);
dp[0] = true;
// 求排列数, 所有先遍历背包容量, 再遍历物品
for(int i = 0; i <=s.length(); i++){
for(int j = 0; j < i; j++){
if(dp[j] && set.contains(s.substring(j, i))){
dp[i] = true;
}
}
// System.out.println(Arrays.toString(dp));
}
return dp[s.length()];
}
}
// 另一种思路的背包算法
class Solution {
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) {
int len = word.length();
if (i >= len && dp[i - len] && word.equals(s.substring(i - len, i))) {
dp[i] = true;
break;
}
}
}
return dp[s.length()];
}
}