前言:最近开始学习数据结构与算法,顺便刷leetcode,无意间发现了一个我觉得讲解的比较好的网页,并且会那leetcode里面的题目当做例题,如有需要,奉上网址:五分钟学算法
一、基本概念
1.什么是动态规划
一句话概括:记住你之前做过的事情—记住你之前的答案。
2.动态规划问题的四个步骤
①问题拆解,找到问题之间的具体联系。
②状态定义,拆解出来的小问题之间的联系与什么状态相关联。
③递推方程推导。
④实现。
二、初级题目实战
1.爬楼梯(Leetcode第70号问题)
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
问题拆解:
我们到达第 n 个楼梯可以从第 n - 1 个楼梯和第 n - 2 个楼梯到达,因此第 n 个问题可以拆解成第 n - 1 个问题和第 n - 2 个问题,第 n - 1 个问题和第 n - 2 个问题又可以继续往下拆,直到第 0 个问题,也就是第 0 个楼梯 (起点)
状态定义
“问题拆解” 中已经提到了,第 n 个楼梯会和第 n - 1 和第 n - 2 个楼梯有关联,那么具体的联系是什么呢?你可以这样思考,第 n - 1 个问题里面的答案其实是从起点到达第 n - 1 个楼梯的路径总数,n - 2 同理,从第 n - 1 个楼梯可以到达第 n 个楼梯,从第 n - 2 也可以,并且路径没有重复,因此我们可以把第 i 个状态定义为 “从起点到达第 i 个楼梯的路径总数”,状态之间的联系其实是相加的关系。
递推方程
“状态定义” 中我们已经定义好了状态,也知道第 i 个状态可以由第 i - 1 个状态和第 i - 2 个状态通过相加得到,因此递推方程就出来了 dp[i] = dp[i - 1] + dp[i - 2]
实现
你其实可以从递推方程看到,我们需要有一个初始值来方便我们计算,起始位置不需要移动 dp[0] = 0,第 1 层楼梯只能从起始位置到达,因此 dp[1] = 1,第 2 层楼梯可以从起始位置和第 1 层楼梯到达,因此 dp[2] = 2,有了这些初始值,后面就可以通过这几个初始值进行递推得到。
class Solution {
public:
int climbStairs(int n) {
if(n<=3)
return n;
int dp[n+1];
dp[1]=1;
dp[2]=2;
for(int i=3;i<=n;i++)
{
dp[i]=dp[i-1]+dp[i-2];
}
return dp[n];
}
};
2.三角形最小路径和(LeetCode 第 120 号问题)
步骤同上一题
本体递推方程:dp[i][j] = Math.min(dp[i + 1][j], dp[i + 1][j + 1]) + triangle[i][j]
class Solution {
public:
int minimumTotal(vector<vector<int>>& triangle) {
if(triangle.empty())
return 0;
int L=triangle.size();
for(int i=1;i<L;++i)
for(int j=0;j<triangle[i].size();++j)
{
int right=(j<triangle[i-1].size())? triangle[i-1][j]:INT_MAX;
int left=(j>=1&&j-1<triangle[i-1].size())? triangle[i-1][j-1]:INT_MAX;
triangle[i][j]+=(right<left)? right:left;
}
int res=INT_MAX;
for(int j=0;j<triangle[L-1].size();++j)
res=(triangle[L-1][j]<res)? triangle[L-1][j]:res;
return res;
}
3.最大子序和(LeetCode 第 53 号问题)
本体递推方程:dp[i] = max(dp[i - 1], 0) + array[i]
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int amax=nums[0];
int bmax=nums[0];
int length=nums.size();
for(int i=1;i<length;i++)
{
if(bmax>0)
bmax+=nums[i];
else
bmax=nums[i];
amax=max(amax,bmax);
}
return amax;
}
};