引言:
动态规划是最重要、最经典的算法之一,学好动态规划对我们十分重要,掌握动态规划对解决某些问题会起到事半功倍的效果。
动态规划:
特点:
①可以把原始问题划分为一系列子问题
②求解每个子问题仅一次,并将其结果保存到一个表中,以后用到时直接存取,不重复计算,节省时间。
③自底向上地计算
适用范围:
原问题可以分为多个相关子问题,子问题的解会被重复使用。
动态规划题目的特点:
1.计数
-有多少种方式走到右下角
-有多少种方法选出k个数的和为sum
2.求最大最小值
-从左上角到右下角路径的最大数字和
-最长上升子序列长度
3.求存在性
-取石子游戏,先手是否必胜
-能不能选出k个数使得和是sum
下面我会用一些力扣上的动态规划经典例题来解释动态规划的用法。请大家将题目和上面的问题特点对号入座。
题目1:零钱兑换
给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
题解:
这道题可以从前往后推,如果目标钱数是0,凑成总金额所需的最少的硬币个数是多少;
目标是1,会怎样呢,…直到目标钱数为amount,需要的最少硬币又是多少呢?
首先要看最后一步,最后一步肯定是dp[i]置为dp[i-coins[j]]+1中最小的那个。所以转换为正推,
如果无法凑够则先把硬币数dp[i]置为最大INT_MAX;否则,则把dp[i]置为最小的dp[i-coins[j]]+1.
然后最后判断一下,如果dp[amount]是否为INT_MAX,如果是,说明无法凑齐,返回-1,如果不是,说明dp[amount]就是最少硬币数。
重要的就是转移方程:dp[i]=min(dp[i],dp[i-coins[j]]+1);
代码:
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
int dp[amount+1];//因为下面dp[i]=INT_MAX,如果再加1,就会超过范围
dp[0]=0;
int i,j;
for(i=1;i<=amount;i++)
{
dp[i]=INT_MAX-10000;
for(j=0;j<coins.size();j++)
{
if(i>=coins[j])
{
dp[i]=min(dp[i],dp[i-coins[j]]+1);
}
}
}
return dp[amount]==INT_MAX?-1:dp[amount];
}
};
题目2:不同路径
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
问总共有多少条不同的路径?
题解:
我们首先看最后一步,要想到达(m,n)首先要到达(m-1,n)或者(m,n-1)。设到达(m-1,n)路径有x种,到达(m,n-1)路径有y种,则到达(m,n)的路径有x+y种。
然后再从头看起,到达(1,2)路径只有1种,到达(2,1)路径只有1种,到达(i,j)的路径数是到达(i-1,j)和(i,j-1)的路径和。
重点:转移方程:dp[i][j]=dp[i-1][j]+dp[i][j-1];
代码:
class Solution {
public:
int uniquePaths(int m, int n) {
int dp[m][n];
int i,j;
dp[0][0]=0;
for(i=0;i<m;i++)
{
for(j=0;j<n;j++)
{
if(i==0||j==0)
{
dp[i][j]=1;
}
else
{
dp[i][j]=dp[i-1][j]+dp[i][j-1];
}
}
}
return dp[m-1][n-1];
}
};
题目3:跳跃游戏
给定一个非负整数数组,你最初位于数组的第一个位置。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个位置。
题解:
我们首先从后面看,要想到达下标为n-1的地方,有两个条件:①能到达下标为 i 的地方;②从 i 能调到 j 。所以我们就得到了转移方程:if(dp[j]&&j+nums[j]>=i) dp[j]=true;
然后我们再从头遍历,判断每个下标为 i 的地方算法能调到,最后返回dp[n-1]即可。
代码:
class Solution {
public:
bool canJump(vector<int>& nums) {
int i,j;
bool dp[nums.size()];
dp[0]=true;
for(i=1;i<nums.size();i++)
{
dp[i]=false;
for(j=0;j<i;j++)
{
if(dp[j]&&j+nums[j]>=i)
{
dp[i]=true;
break;
}
}
}
return dp[nums.size()-1];
}
};
题目4:背包问题
因为背包问题比较重要,所以我单独把背包问题拿出来单独写一篇博客。
传送门