赞
踩
1. 完全背包:物品是wordDict中的单词,背包是s;
2. dp数组及其下标的含义:dp[i]表示长度为 i 的字符串是否可以被拆分为 wordDict 中的单词;dp[i] = 1表示可以拆分,dp[i] = 0表示不能拆分;
3. 递推公式:当j < i时,若dp[j] = 1,且 [j,i] 区间的子串出现在字典里,那么 dp[i] = 1。
4. 初始化:dp[0] = 1(dp[0]是递推起点),下标非0的 dp[i] 初始化为false,只要没被覆盖,就说明不可拆分。
5. 遍历顺序:求排列数,外层for循环遍历背包,内层for循环遍历物品。
- class Solution {
- public boolean wordBreak(String s, List<String> wordDict) {
- HashSet<String> set = new HashSet<>(wordDict);
- int[] dp = new int[s.length()+1];
- dp[0] = 1;
- for(int i=1;i<=s.length();i++){
- for(int j=0;j<i && dp[i]==0;j++){//dp[i]==0 剪枝
- String sub = s.substring(j,i);
- if(set.contains(sub) && dp[j]==1){
- dp[i]=1;
- break;
- }
- }
- }
- return dp[s.length()]==1;
- }
- }
使用memo数组保存每次计算的以startIndex为起点的计算结果。如果memo[startIndex]已经被标记,那么直接返回memory[startIndex]的结果。
- class Solution {
- private Set<String> set;
- private int[] memo;
- public boolean wordBreak(String s, List<String> wordDict) {
- memo = new int[s.length()];
- set = new HashSet<>(wordDict);
- return backTracking(s,0);
- }
- public boolean backTracking(String s, int startIndex){
- if(startIndex==s.length()) return true;
- if(memo[startIndex]==-1) return false;
- for(int i=startIndex;i<s.length();i++){
- String sub = s.substring(startIndex,i+1);
- if(!set.contains(sub)){//拆分出来的单词无法匹配
- continue;
- }
- boolean res = backTracking(s,i+1);
- if(res) return true;
- }
- //找遍了startIndex~s.length()也没能完全匹配,标记从startIndex开始不能找到
- memo[startIndex] = -1;
- return false;
- }
- }
1. 问题形式:有N种物品和一个容量为V 的背包。第i种物品最多有Mi件可用,每件耗费的空间是Ci ,价值是Wi 。求将哪些物品装入背包,可使耗费的空间不超过背包容量,且价值总和最大。
2. 思路:转化为01背包问题。每件物品最多有Mi件可用,那就把Mi件摊开,每件的数量是1。
- //版本一:改变物品数量为01背包格式
- public void testMultiPack1(){
- 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 = {1, 3, 4};
- int[] value = {15, 20, 30};
- int[] nums = {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--) { //遍历背包容量
- //遍历个数
- 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));
- }
- }
- }
背包递推公式:
1. 能否装满背包(或者最多能装多少):dp[j] = max(dp[j],dp[j-nums[i]]+nums[i]);
2. 装满背包有几种方法:dp[j] += dp[j-nums[i]];
3. 装满背包,最大价值:dp[j] = max(dp[j],dp[j-nums[i]]+value[i]);
4. 装满背包的最小物品数目:dp[j] = min(dp[j-coins[i]]+1, dp[j]);
遍历顺序:
1. 01背包:
(1)二维dp数组,先遍历物品再遍历背包,或者先遍历背包再遍历物品都可以,且第二层for循环从小到大;
(2)一维dp数组,只能先遍历物品再遍历背包容量,且第二层for循环从大到小;
2. 完全背包:
(1)纯完全背包,先遍历物品或先遍历背包都可以,且第二层for循环从小到大。
(2)求组合数,外层for循环遍历物品,内层for遍历背包。
(3)求排列数,外层for遍历背包,内层for循环遍历物品。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。