讲解链接:
代码随想录
动态规划之完全背包,装满背包最少的物品件数是多少?| LeetCode:322.零钱兑换_哔哩哔哩_bilibili
动态规划之完全背包,换汤不换药!| LeetCode:279.完全平方数_哔哩哔哩_bilibili
57. 爬楼梯
思路:递推公式dp[j]+=dp[j-nums[i]]的练习 ,这里爬楼梯的来源从2变成了m,所以求和m个位置即可。(类别:放满完全背包)
import java.util.*;// 时间复杂度O(n^2)
// 空间复杂度O(n)public class Main {public static void main(String[] args) {// 如果一次可以爬m个台阶,那么爬楼梯共可以有m个来源,应当将这m个来源的都求和Scanner scanner = new Scanner(System.in);int n = scanner.nextInt();int m = scanner.nextInt();// 每次可以上几层楼梯是物品,待攀爬的楼梯数是重量// dp表示当前i级楼梯可由几种方式进行攀爬int[] dp = new int[n+1];dp[0] = 1;// 采用排列的方式进行遍历 for(int i=1; i<=n; i++){for(int j=1; j<=m; j++)if(i-j>=0){dp[i] += dp[i-j];}}System.out.println(dp[n]);}
}
322. 零钱兑换
思路:延续零钱兑换II 的做法,统计某个数值可以被找零,是求的组合,所以本题的递推公式也是包含累加的思想,但是由于统计的是硬币数量,因此在每判断一种硬币后,是将j-coins[i[的位置的值加1得到dp[j]位置的值。区别dp[j] += dp[j-coins[i]]和dp[j] = dp[j-coins[i]]+1;其关键的不同之处在于两个dp数组分别表示的是方法数以及硬币数量,但是每个位置所关联的是前面的若干位置这一点没有错误。(类型标记:完全背包问题之放满背包的最少物件数量,不存在不可放的情况,数组需要额外统一初始化)
class Solution {public int coinChange(int[] coins, int amount) {// 采用组合的遍历方式// 每种硬币可视作为物品// dp数组表示数值为i时可由几枚硬币组成,不能组成的采用离群值进行表示int[] dp = new int[amount+1];Arrays.fill(dp, Integer.MAX_VALUE);dp[0] = 0;for(int i=0; i<coins.length; i++){for(int j=coins[i]; j<=amount; j++){if(dp[j-coins[i]] == Integer.MAX_VALUE) // 不能表示的具有传递性,但是只能依据前一位置能否表示判断当前位置的某一条路走不走的通,或许还会有其他的路来实现当前位置的赋值,另外就算前一位置无法表示,也不能更改当前位置的值为MAX_VALUE,这种覆盖操作会将先前已保留的某一种可行的值替换成了不可行的值continue;elsedp[j] = Math.min(dp[j], dp[j-coins[i]]+1);}}if(dp[amount] == Integer.MAX_VALUE)return -1;return dp[amount];}
}
279.完全平方数
思路:每一个完全平方数都可以无限次的被使用,所以每一个完全平方数是物品,而每一个待分析的数视作为重量;递推公式与找零的递推公式保持一致,使用的是减去一个当前可用物品后加一。(类型标记:完全背包问题之放满背包的最少物件数量,不存在不可放的情况,数组无需额外统一初始化)
// 时间复杂度O(n^2)
// 空间复杂度O(n)class Solution {public int numSquares(int n) {int[] dp = new int[n+1];Arrays.fill(dp, Integer.MAX_VALUE);dp[0] = 0;for(int i=1; i<=n; i++){if(!isPerfectSquare(i)) continue;for(int j=i; j<=n; j++)dp[j] = Math.min(dp[j], dp[j-i]+1);}return dp[n];}public boolean isPerfectSquare(int num) {int sqrt = (int) Math.sqrt(num);return sqrt * sqrt == num;}
}