前言
一直都搞不太懂动态规划,不过在这里从简单的两个题入手来探索动态规划的规律。
一、动态规划是什么?
我理解的动态规划,就是自下向上进行讨论,将每种情况的最优解记录并保存。
二、例题:凑硬币问题
1.题目
题目来源点这里: leetcode322 凑硬币问题.
给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
你可以认为每种硬币的数量是无限的。
示例 1:
输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1
2.算法原理
先解释一下一些一会代码中会用到的一些符号的意义:
①amount为最终需要凑的总金额
②coins[j]表示可能用到的硬币金额
③dp[i]从1遍历到amount表示每次凑i元所需要的最少的硬币数
算法思想:
①i=1时,首先考虑凑1元的情况,只能是一个1元硬币,所以dp[1]=1;
②i=2时,凑2元的硬币数为dp[2]=min(dp[2],dp[2-1]+1),表示的就是取选不选1元硬币时候的最少的硬币数;
③i=3时,凑3元硬币数的dp[3]=min(dp[3],dp[3-2]+1,dp[3-1]+1),表示就是取选哪一枚硬币时候用到的最少的硬币数;
……
归纳一下,从凑1元硬币的时候开始,每次把计算出来所需要的最少的硬币数存入到dp[i]中,而凑的硬币数不断增加,遍历所有最后一个取的硬币数,取拿这个硬币dp[i-coins[j]]+1和dp[i]之间的最小值。
3.代码实现
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
int MAX = amount+1;
vector<int> dp(MAX,amount+1);
dp[0]=0;
for(int i=1;i<=amount;i++){
for(int j=0;j<coins.size();j++){
if(coins[j]<=i)
dp[i] = min(dp[i],dp[i-coins[j]]+1);
}
}
return dp[amount]>amount?-1:dp[amount];
}
};
三、例题:变态青蛙跳台阶问题
1.题目
题目来源点这里: 剑指offer第九题.
2.算法原理
①从跳1阶台阶开始考虑,只有一种情况dp[1]=1;
②跳2阶台阶,有两种情况,最后一次跳2阶和最后一次跳1阶
dp[2] = dp[2]+dp[1];
③跳3阶台阶,有三种情况,最后一次跳3阶、最后一次跳2阶、最后一次跳1阶;
dp[3]=dp[3]+dp[2]+dp[1];
依次类推。
3.代码实现
class Solution {
public:
int jumpFloorII(int number) {
int max = number+1;
vector<int> dp(max,1); //dp表示跳n阶台阶一共有多少种跳法
dp[0]=0;
for(int i=1;i<=number;i++){
for(int j=1;j<i;j++){
dp[i] += dp[i-j];
}
}
return dp[number];
}
};
总结
动态规划可以先从比较小的情况开始考虑,再总结规律。