写在前面
这次算法训练营题目,其实完全是按照代码随想录 一路跟着来的,上面也有更好的、讲得更清楚的题解,有需要的小伙伴可以去那里看。
我这里是之前已经大体刷过一遍,为了应对有可能会考到的面试题,现在在跟着一个专门的、要花钱的训练营补完笔记,加深理解。
下面开始今天的刷题和笔记。
卡码网 52
这题是完全背包问题的典型,完全背包也就是每个物品可以取无限次。
如果用二维dp的话,我们每个物品都要再加一个在能取次数内几个次数的循环比较。
而用一维dp数组的内层正向遍历就能很好地解决这个问题,所以我们修改下内层遍历顺序就行了。
代码如下:
java
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int bagSize = in.nextInt();
int[] w = new int[n];
int[] v = new int[n];
for (int i = 0; i < n; i++) {
w[i] = in.nextInt();
v[i] = in.nextInt();
}
int[] dp = new int[bagSize + 1];
for (int i = 0; i < n; i++) {
for (int j = 0; j <= bagSize; j++) {
if (j >= w[i])
dp[j] = Math.max(dp[j], dp[j - w[i]] + v[i]);
}
}
System.out.println(dp[bagSize]);
}
}
LeetCode 518 零钱兑换II
这题基本还是完全背包的思路,不过是计算方法数类型的问题。需要改动的地方主要是初始化这一块,要将dp[0]置为1,然后利用滚动数组的特性,可以让每种方法都计数为1。
代码如下:
java
class Solution {
public int change(int amount, int[] coins) {
int[] dp = new int[amount + 1];
dp[0] = 1;
for (int i = 0; i < coins.length; i++) {
for (int j = 0; j <= amount; j++) {
if (j >= coins[i])
dp[j] += dp[j - coins[i]];
}
}
return dp[amount];
}
}
如果是二维dp数组的话,每一轮中的dp[i][0]都要设置为1。递推逻辑也要稍微变化下。
代码如下:
java
class Solution {
public int change(int amount, int[] coins) {
int[][] dp = new int[coins.length][amount + 1];
for (int i = 0; i < coins.length; i++) dp[i][0] = 1;
for (int i = 0; i < coins.length; i++) {
for (int j = 0; j <= amount; j++) {
if (j >= coins[i])
if (i == 0) dp[i][j] += dp[i][j - coins[i]];
else
dp[i][j] = dp[i - 1][j] + dp[i][j - coins[i]];
else
if (i == 0) continue;
else
dp[i][j] = dp[i - 1][j];
}
}
return dp[coins.length - 1][amount];
}
}
LeetCode 377 组合总和IV
这题是排列问题,上面一题是组合问题。两个问题在遍历顺序上有较大差别。这是方法数问题在dp这一块的特殊问题,如果只是单纯的求总价值的话就没有这个问题了。
解决方法是将内重外重循环反过来,这样在每个背包重量下,放入某个价值的物品最后构成的不同顺序的方法都会被计算进去。
比如这组数据
两重循环顺序反过来后就是这样
原先是这样
确实可以看出由遍历顺序引起的放入次数的问题,这是由于原先总是按照物品下标从小到大放入,顺序倒转后有可能下标较大的物品先被放入造成了次序上的不一致。而且也可以看出这其实也是完全背包这一块的特殊问题,01背包问题很少会问到排列。
代码如下:
java
class Solution {
public int combinationSum4(int[] nums, int target) {
int[] dp = new int[target + 1];
dp[0] = 1;
for (int j = 0; j <= target; j++) {
for (int i = 0; i < nums.length; i++) {
if (j >= nums[i])
dp[j] += dp[j - nums[i]];
System.out.print(dp[j] + " ");
}
System.out.println();
}
return dp[target];
}
}