我报名参加金石计划一期挑战——瓜分10万奖池,这是我的第1篇文章,点击查看活动详情
什么是动态规划
动态规划是一种解决问题的思想。
要想解决目标问题A,可以先解决问题B。
B问题和A问题是相似的,区别只在于B问题的入参规模小。
B问题的解决有助于A问题的解决。
举个例子
斐波那契数列。
第一个数和第二个数都是是1,后面的数都是前两个数的和,求第n个数是多少?
1 1 2 3 5 8 13
可以先求出第三个数,进而求第四个数,最后解决第n个数。
public int fib(int n) {
int[] dp = new int[n + 2];
dp[0] = 0;
dp[1] = 1;
for (int i = 2; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
动态规划的关键点是状态的定义和状态转移方程。
状态定义: dp[i] 代表第i个数。
状态转移方程: dp[i] = dp[i-1] + dp[i-2]
再看个例子。
给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。
计算并返回可以凑成总金额所需的最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。
你可以认为每种硬币的数量是无限的。
示例 1:
输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1
示例 2:
输入:coins = [2], amount = 3
输出:-1
示例 3:
输入:coins = [1], amount = 0
输出:0
看示例1。
思路:
目标问题是凑成面值11所需要的最少硬币个数。
先考虑凑成面值1的、继而2的、3的、直至最终目标11
凑成面值为1:取面值为1的硬币1个
凑成面值为2:取面值为2的硬币1个
凑成面值为3:如果最后一枚的硬币面值是1,凑成面值为2的结果+1是其中一种情况
如果最后一枚的硬币面值是2,凑成面值为1的结果+1是其中一种情况
......
枚举最后一枚硬币的面值,结合之前的答案,能很快找出最小的次数
状态定义:dp[i] 为凑成金额i的最少硬币个数
状态转移方程:
j为硬币面值枚举
public int coinChange(int[] coins, int amount) {
int max = amount + 1;
int[] dp = new int[amount + 1];
Arrays.fill(dp, max);
dp[0] = 0;
for (int i = 1; i <= amount; i++) {
for (int j = 0; j < coins.length; j++) {
if (coins[j] <= i) {
dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1);
}
}
}
return dp[amount] > amount ? -1 : dp[amount];
}
\