322. 零钱兑换
如果求组合数就是外层for循环遍历物品,内层for遍历背包。
如果求排列数就是外层for遍历背包,内层for循环遍历物品。
钱币有顺序和没有顺序都可以,都不影响钱币的最小个数。
视频讲解:动态规划之完全背包,装满背包最少的物品件数是多少?| LeetCode:322.零钱兑换_哔哩哔哩_bilibili
class Solution {
public int coinChange(int[] coins, int amount) {
if(amount == 0)return 0;
int bagSize = amount;
int[] dp = new int[amount + 1];
Arrays.fill(dp,Integer.MAX_VALUE);//后面要取最小值,初始值都要设成最大
dp[0] = 0;
for(int i = 0;i <= bagSize;i++){
for(int j = 0;j < coins.length;j++){
if(i >= coins[j] && dp[i - coins[j]] != Integer.MAX_VALUE){
dp[i] = Math.min(dp[i],dp[i - coins[j]] + 1);//初始值加一会越界
}
}
}
if(dp[amount] == Integer.MAX_VALUE)return -1;
return dp[amount];
}
}
279.完全平方数
视频讲解:动态规划之完全背包,换汤不换药!| LeetCode:279.完全平方数_哔哩哔哩_bilibili
class Solution {
public int numSquares(int n) {
int[] dp = new int[n + 1];
Arrays.fill(dp,Integer.MAX_VALUE);
dp[0] = 0;
dp[1] = 1;
for(int i = 1;i*i <= n;i++){
int s = i * i;
for(int j = s;j <= n;j++){
if(dp[j - s] != Integer.MAX_VALUE){
dp[j] = Math.min(dp[j],dp[j - s] + 1);
}
}
}
return dp[n];
}
}
139.单词拆分
视频讲解:动态规划之完全背包,你的背包如何装满?| LeetCode:139.单词拆分_哔哩哔哩_bilibili
dp[i]代表,数组元素是否能够组成长度为i时的s
完全背包,正序遍历
对顺序有要求,先背包再物品
递推公式:若前面的字符串可以被组合出来,同时后面的字符串也在数组元素里
int len = word.length();
if(i >= len && dp[i - len] && set.contains(s.substring(i - len,i))){
dp[i] = true;
break;
}
初始化:dp[0] = true;
class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
Set<String> set = new HashSet<>(wordDict);
boolean[] dp = new boolean[s.length() + 1];
dp[0] = true;
for(int i = 1;i < s.length() + 1;i++){
for(String word:set){
int len = word.length();
if(i >= len && dp[i - len] && set.contains(s.substring(i - len,i))){
dp[i] = true;
break;
}
}
}
return dp[s.length()];
}
}
关于多重背包,你该了解这些!
转化为01背包
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int bagSize,n;
bagSize = sc.nextInt();
n = sc.nextInt();
int[] weight = new int[n];
int[] value = new int[n];
int[] nums = new int[n];
for(int i = 0;i < n;i++)weight[i] = sc.nextInt();
for(int i = 0;i < n;i++)value[i] = sc.nextInt();
for(int i = 0;i < n;i++)nums[i] = sc.nextInt();
int[] dp = new int[bagSize + 1];
for(int i = 0;i < n;i++){
for(int j = bagSize;j >= weight[i];j--){
for(int k = 1;k <= nums[i];k++){
if(j >= k * weight[i])dp[j] = Math.max(dp[j],dp[j - k * weight[i]]
+ k * value[i]);
}
}
}
System.out.println(dp[bagSize]);
}
}
背包问题总结
五部曲:
- 确定dp数组(dp table)以及下标的含义
- 确定递推公式
- dp数组如何初始化
- 确定遍历顺序
- 举例推导dp数组
背包递推公式
问能否能装满背包(或者最多装多少):dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);
问装满背包有几种方法:dp[j] += dp[j - nums[i]]
问背包装满最大价值:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
问装满背包所有物品的最小个数:dp[j] = min(dp[j - coins[i]] + 1, dp[j]);
遍历顺序
01背包
二维dp数组01背包先遍历物品还是先遍历背包都是可以的,且第二层for循环是从小到大遍历。
一维dp数组01背包只能先遍历物品再遍历背包容量,且第二层for循环是从大到小遍历。
完全背包
纯完全背包的一维dp数组实现,先遍历物品还是先遍历背包都是可以的,且第二层for循环是从小到大遍历。
但是仅仅是纯完全背包的遍历顺序是这样的,题目稍有变化,两个for循环的先后顺序就不一样了。
如果求组合数就是外层for循环遍历物品,内层for遍历背包。
如果求排列数就是外层for遍历背包,内层for循环遍历物品。
如果求最小数,那么两层for循环的先后顺序就无所谓了