一、70. 爬楼梯 (进阶)
了解到了爬楼梯新的解法,感觉这种解法其实更容易理解。
之所以可以转化:
- 可以选择的物品 0、1
- 可以选择任意多次,数量不限
- 有一个目标值——最终的楼梯阶数
所以是完美契合使用完全背包来求解排序的种类数。
以下是代码部分:
//思路: 完全背包,求排序种类
public int climbStairs(int n) {
int[] dp = new int[n+1];
dp[0] = 1;
int[] nums = new int[]{1,2};
for (int i = 0; i < n+1; i++) {
for (int j = 0; j < nums.length; j++) {
//踩坑:这里时小于等于
if( nums[j] <= i)
dp[i] += dp[i - nums[j]];
}
}
for (int i = 0; i < n+1; i++) {
System.out.print(dp[i] + " ");
}
return dp[n];
}
二、322. 零钱兑换
这道题需要注意的地方:
- dp[]数组表示取得 i 值所需的最少金币数 dp[i]
- 递推公式是进行一个比较,比较的双方是:不加当前物品 i 所得的dp[j]、 加上当前物品 i 所得的dp[j - coins[i] ] + 1。注意这里有 +1 ,因为已经是算上当前物品了。
- 初始化时,dp[0] = 0,表示0面值不需要金币,所以是0个。但其他数值要初始化成最大的整型,因为后续要比较大小,这里若初始化为0为影响到比较结果。
- 最需要注意的一点:如果dp[j - coins[i]] 为初始值,则跳过,证明没有方案可以实现价值等于j - coins[i] 。
以下是代码部分:
public class 零钱兑换322 {
public int coinChange(int[] coins, int amount) {
//表示取得当前面值所需的最少金币数
int[] dp = new int[amount+1];
//0面值不需要金币,所以是0个
dp[0] = 0;
//其他数要初始化为最大的整型,后续判断要取小的值,防止影响结果
for (int i = 1; i < dp.length; i++) {
dp[i] = Integer.MAX_VALUE;
}
//排列、组合都可以。因为是求最少的金币数,而不是去求总的种类数
for (int i = 0; i < coins.length; i++) {
for (int j = coins[i]; j < amount+1; j++) {
//踩坑:如果减去coins[i]为初始值,则跳过,证明没有方案可以实现价值等于j - coins[i]
if( dp[j - coins[i]] != Integer.MAX_VALUE )
dp[j] = Math.min(dp[j], dp[j - coins[i]] + 1); //上一行的dp[j](不加物品i)和当前行加上物品i的dp[i]进行比较
}
}
//这里还要判断最后的值是否依旧是原始值,若是,证明没有可行方案
if( dp[amount] == Integer.MAX_VALUE)
return -1;
return dp[amount];
}
}
三、279.完全平方数
这道题其实跟上道题一样,唯一不同的点:
这道题可以不需要判断上题的第4点,因为 1*1 肯定会组成任意一个数,所以每个值在使用1的时候都会被初始化了。即,不存在无解的情况。
以下是代码部分:
public class 完全平方数279 {
public int numSquares(int n) {
//组成i至少需要dp[i]个数
int[] dp = new int[n+1];
//0需要0个数
dp[0] = 0;
//其他数值初始化为最大值
for (int i = 1; i < dp.length; i++) {
dp[i] = Integer.MAX_VALUE;
}
for (int i = 1; i <= Math.sqrt(n); i++) {
for (int j = i*i; j < n+1; j++) {
//这里可以不用判断,因为 1*1 肯定会组成任意一个数,所以每个值在使用1的时候依旧被初始化了。
if( dp[j - i*i] != Integer.MAX_VALUE)
dp[j] = Math.min(dp[j], dp[j - i*i] + 1);
}
}
return dp[n];
}
}