52. 携带研究材料
题解:代码随想录
状态:需要多复习
思路
- dp数组含义:dp[i][j]表示从下标为[0-i]的物品,各物品可以取无限次,放进容量为j的背包,价值总和最大值
- 递推公式:不放物品i对比放物品i,dp[i][j] = max(dp[i - 1][j], dp[i][j - weight[i]] + value[i])
- 初始化:由递推公式知,需初始化dp[i][0]=0,以及dp[0][j]
代码
时间复杂度:O(N*V) 空间复杂度:O(N*V)
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int v = in.nextInt();
int[] weight = new int[n];
int[] value = new int[n];
for(int i = 0; i < n; i++){
weight[i] = in.nextInt();
value[i] = in.nextInt();
}
int[][] dp = new int[n][v + 1];
for(int i = weight[0]; i <= v; i++){
dp[0][i] = dp[0][i - weight[0]] + value[0];
}
for(int i = 1; i < n; i++){
for(int j = 1; j <= v; j++){
if(j < weight[i]) dp[i][j] = dp[i - 1][j];
else dp[i][j] = Math.max(
dp[i][j - weight[i]] + value[i],
dp[i - 1][j]
);
}
}
System.out.println(dp[n - 1][v]);
}
}
518.零钱兑换II
题目:518. 零钱兑换 II - 力扣(LeetCode)
题解:代码随想录
状态:AC,一维数组的情况需要复习
思路
二维
- 递推公式:dp[i][j] = dp[i - 1][j] + dp[i][j - coins[i]]
- dp数组初始化:第一列初始化为1和第一行初始化
- 遍历顺序:二维的遍历顺序随便
一维
- 递推公式:dp[j] += dp[j - coins[i]]
- dp数组初始化:dp[0] = 1,表示amount为0时有一种找零方式,即不找零
- 遍历顺序:先种类再容量为组合数,否则为排列数
代码(二维dp数组)
时间复杂度:O(N*Amount) 空间复杂度:O(N*Amount)
class Solution {
public int change(int amount, int[] coins) {
int n = coins.length;
int[][] dp = new int[n][amount + 1];
for(int i = 0; i < n; i++){
dp[i][0] = 1;
}
for(int i = coins[0]; i <= amount; i+=coins[0]){
dp[0][i] = 1;
}
for(int i = 1; i < n; i++){
for(int j = 1; j <= amount; j++){
if(j < coins[i]) dp[i][j] = dp[i - 1][j];
else dp[i][j] = dp[i - 1][j] + dp[i][j - coins[i]];
}
}
return dp[n - 1][amount];
}
}
代码(一维dp数组)
时间复杂度:O(N*Amount) 空间复杂度:O(Amount)
class Solution {
public int change(int amount, int[] coins) {
int n = coins.length;
int[] dp = new int[amount + 1];
dp[0] = 1;
for(int i = 0; i < n; i++){
for(int j = coins[i]; j <= amount; j++){
dp[j] += dp[j - coins[i]];
}
}
return dp[amount];
}
}
377. 组合总和 Ⅳ
题解:代码随想录
状态:需要多复习
思路
与组合数的区别在于遍历顺序,此处是先背包容量,再硬币种类
代码
时间复杂度:O(N^2) 空间复杂度:O(N)
class Solution {
public int combinationSum4(int[] nums, int target) {
int[] dp = new int[target + 1];
dp[0] = 1;
for(int i = 1; i <= target; i++){
for(int j = 0; j < nums.length; j++){
if(i >= nums[j]){
dp[i] += dp[i - nums[j]];
}
}
}
return dp[target];
}
}
57. 爬楼梯
题解:代码随想录
状态:AC
思路
同上题
代码
时间复杂度:O(N^2) 空间复杂度:O(N)
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int m = in.nextInt();
int[] dp = new int[n + 1];
dp[0] = 1;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j ++){
if(j <= i) dp[i] += dp[i - j];
}
}
System.out.println(dp[n]);
}
}
322. 零钱兑换
题解:代码随想录
状态:半AC
思路
需要注意将无法凑出的数替换成amount+1
代码
时间复杂度:O(N^2) 空间复杂度:O(N)
class Solution {
public int coinChange(int[] coins, int amount) {
if(amount == 0) return 0;
int n = coins.length;
int[] dp = new int[amount + 1];
Arrays.fill(dp, amount + 1);
dp[0] = 0;
for(int i = 0; i < n; i++){
for(int j = coins[i]; j <= amount; j++){
dp[j] = Math.min(dp[j], dp[j - coins[i]] + 1);
}
}
return dp[amount] > amount ? -1 : dp[amount];
}
}
279. 完全平方数
题解:代码随想录
状态:需要多复习
思路
注意初始化和遍历条件
代码
时间复杂度:O(N^2) 空间复杂度:O(N)
class Solution {
public int numSquares(int n) {
int[] dp = new int[n + 1];
Arrays.fill(dp, n + 1);
dp[0] = 0;
for(int i = 1; i * i <= n; i++){
for(int j = i * i; j <= n; j++){
dp[j] = Math.min(dp[j], dp[j - i * i] + 1);
}
}
return dp[n];
}
}
139.单词拆分
题解:代码随想录
状态:需要多复习
思路
- dp[i] : 字符串长度为i的话,dp[i]为true,表示可以拆分为一个或多个在字典中出现的单词。
- if([j, i] 这个区间的子串出现在字典里 && dp[j]是true) 那么 dp[i] = true
- dp[i] 的状态依靠 dp[j]是否为true,那么dp[0]就是递推的根基,dp[0]一定要为true,否则递推下去后面都都是false
- 相当于求排列数的变体,所以要先遍历背包再遍历物品
代码
时间复杂度:O(N^2) 空间复杂度:O(N)
class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
HashSet<String> set = new HashSet<>(wordDict);
boolean[] valid = new boolean[s.length() + 1];
valid[0] = true;
for (int i = 1; i <= s.length(); i++) {
for (int j = 0; j < i && !valid[i]; j++) {
if (set.contains(s.substring(j, i)) && valid[j]) {
valid[i] = true;
}
}
}
return valid[s.length()];
}
}