1 零钱兑换
给你一个整数数组 coins
,表示不同面额的硬币;以及一个整数 amount
,表示总金额。
计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1
。
你可以认为每种硬币的数量是无限的。
示例 1:
输入:coins = [1, 2, 5] amount = 11 输出:3 解释:11 = 5 + 5 + 1
示例 2:
输入:coins = [2] amount = 3 输出:-1
思想:动态规划
边界条件:dp[0] = 0
状态转移方程:F(i) = min j=0,1...nF(i-cj) + 1
定义 F(i)为组成金额 i所需最少的硬币数量,假设在计算 F(i) 之前,我们已经计算出 F(0) ~F(i−1)
的答案,其中 cj代表的是第 j枚硬币的面值
代码:
class Solution {
public int coinChange(int[] coins, int amount) {
// 初始化动态规划数组 初始化最大值数组
int max = amount + 1;
int[] dp = new int[amount + 1]; // 数组长度最大为amount+1的原因为: 最坏情况amount= 1+1+...1
// 动态规划数组中填充最大值
Arrays.fill(dp,max);
dp[0] = 0;
// 从1开始遍历目标数值
for(int i = 1; i <= amount; i++){
// 遍历整数数字coins 判断数组中当前面面值是否能组成amount
for(int j = 0; j < coins.length; j++){
// 如果当前数组中面值小于i 进行递归计算 (动态规划方程)
if(coins[j] <= i){
dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1) ;
}
}
}
return dp[amount] > amount ? -1 : dp[amount];
}
}
2 最长递增子序列
可以回顾最长连续序列算法
给你一个整数数组 nums
,找到其中最长严格递增子序列的长度。
子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7]
是数组 [0,3,1,6,2,2,7]
的子序列。
示例 1:
输入:nums = [10,9,2,5,3,7,101,18] 输出:4 解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
思想:动态规划
dp[i] 代表以nums[i] 结尾的最长子序列长度 j ∈ [ 0, i)
- 当nums[i] > nums[j], 表示nums[i]可以接在nums[j] 之后 ,则dp[i] = dp[j] + 1;
- 当nums[i] < nums[j], 跳过
动态转移方程:dp[i] = Math.max(dp[i], dp[j] + 1);
代码:
class Solution {
public int lengthOfLIS(int[] nums) {
// 数组判空
if(nums.length == 0){
return 0;
}
// 初始化动态规划数组 并将值都初始化为1
int length = nums.length;
int[] dp = new int[length + 1];
Arrays.fill(dp, 1);
int result = 1;
// 从1循环遍历数组
for(int i = 1; i < length; i++){
for(int j = 0; j < i; j++){
if(nums[i] > nums[j]){
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
result = Math.max(result, dp[i]);
}
return result;
}
}