递归和动态规划都是将原问题拆成多个子问题然后求解,他们之间最本质的区别是,动态规划保存了子问题的解,避免重复计算。
1、爬楼梯
3、矩阵的最小路径和
思路:创建与grid相同大小的dp表,dp[i][j]表示从(0,0)到(i,j)的最短路径。由于第0行上的值只能由左边的值往右移,第0列的值只能由上面的值往下移。因此第0行第0列的值可直接填入。因为dp[i][j]的值可由dp[i-1][j]下移或dp[i][j-1]右移得到,此时要计算两者之间的最小值,dp[i][j]=min(dp[i-1][j],dp[i][j-1]);此时需要填满整张dp表,最右下角的值即为所求。矩阵一共有M*N个位置,每个位置都要计算一次最小路径,因此时间复杂度为O(M*N),空间复杂度也为O(M*N)。
空间压缩后的动态规划方法:定义一个动态滚动数组,每一行都更新每个位置的最小路径。空间复杂度即为O(N),
class Solution {
public:
int minPathSum(vector<vector<int>>& grid) {
if(grid.empty())
return 0;
int rows=grid.size();
int cols=grid[0].size();
vector<vector<int>>dp;
dp.resize(rows,vector<int>(cols));//初始化dp与grid相同大小
dp[0][0]=grid[0][0];
for(int i=1;i<rows;i++){
dp[i][0]=dp[i-1][0]+grid[i][0];
}
for(int j=1;j<cols;j++){
dp[0][j]=dp[0][j-1]+grid[0][j];
}
for(int i=1;i<rows;i++){
for(int j=1;j<cols;j++){
dp[i][j]=min(dp[i-1][j],dp[i][j-1])+grid[i][j];
}
}
return dp[rows-1][cols-1];
}
};
class Solution {
public:
int minPathSum(vector<vector<int>>& grid) {
if(grid.empty())
return 0;
int rows=grid.size();int cols=grid[0].size();
vector<int>dp(cols);
for(int i=0;i<rows;i++){
for(int j=0;j<cols;j++){
if(j==0)
dp[j]=dp[j];//第一列的值都是从上一行复制下来的
else if(i==0){
dp[j]=dp[j-1];//第一行的值都是左侧的值复制过来的
}
else
dp[j]=min(dp[j-1],dp[j]);
//在上一行中当前位置的值与当前位置的左侧的值的较小值中取较小值
dp[j]+=grid[i][j];
}
}
return dp[cols-1];
}
};
4、矩阵的总路径数
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
问总共有多少条不同的路径?
class Solution {
public:
int uniquePaths(int m, int n) {
if(m<=0||n<=0)
return 0;
vector<vector<int>>dp;
dp.resize(m,vector<int>(n));
for(int i=0;i<m;i++){
dp[i][0]=1;//第一列的每一行只能由上往下到达
}
for(int j=0;j<n;j++){
dp[0][j]=1;//第一行的每一列只能从左向右到达,只有一条路径
}
for(int i=1;i<m;++i){
for(int j=1;j<n;j++){
dp[i][j]=dp[i-1][j]+dp[i][j-1];//可以从左侧进入,也可以从上面进入
}
}
return dp[m-1][n-1];
}
};
5、数组区间和
303. Range Sum Query - Immutable (Easy)
class NumArray {
public:
vector<int>sum;
NumArray(vector<int> nums) {
sum.push_back(0);
for(int i=1;i<=nums.size();i++){
sum.push_back(nums[i-1]+sum[i-1]);
}
}
int sumRange(int i, int j) {
return sum[j+1]-sum[i];
}
};
/**
* Your NumArray object will be instantiated and called as such:
* NumArray* obj = new NumArray(nums);
* int param_1 = obj->sumRange(i,j);
*/
6、等差数列划分
class Solution {
public:
int numberOfArithmeticSlices(vector<int>& A) {
if(A.empty())
return 0;
vector<int>dp(A.size());
for(int i=2;i<A.size();++i){
if(A[i]-A[i-1]==A[i-1]-A[i-2])
dp[i]=dp[i-1]+1;//已i-1结尾的等差数列满足A[i-1]-A[i-2]==A[i-2]A[i-3],因此i,i-1,i-2,i-3也是等差数列,所以以i结尾的等差数列是i-1结尾的等差数列的延伸,加上i与它前面两个数构成的等差数列。
}
int count=0;
for(int t:dp){
count+=t;
}
return count;
}
};
class Solution {
public:
int integerBreak(int n) {
if(n<=0)
return 0;
vector<int>dp(n+1);
dp[1]=1;
dp[2]=1;
for(int i=3;i<=n;i++){
for(int j=1;j<=i-1;j++){
dp[i]=max(dp[i],max(j*dp[i-j],j*(i-j)));
}
}
return dp[n];
}
};
8、v按平方数来分割整数
class Solution {
public:
int numSquares(int n) {
if(n<=0)
return 0;
vector<int>square=Squarenum(n);
vector<int>dp(n+1);
dp[1]=1;
for(int i=2;i<=n;i++){
int minnum=INT_MAX;
for(int j=0;j<square.size();j++){
if(square[j]>i)
break;
minnum=min(minnum,dp[i-square[j]]+1);
}
dp[i]=minnum;
}
return dp[n];
}
vector<int>Squarenum(int n){
int i=1;
int tmp=3;
vector<int>res;
while(i<=n){
res.push_back(i);
i+=tmp;
tmp+=2;
}
return res;
}
};
class Solution {
public:
int juge1(char a){
int sum=(a-'0');
return sum<=26&&sum>0?1:0;
}
int juge2(char a,char b){
int sum=(a-'0')*10+b-'0';
return sum>26||sum<=0||a=='0'?0:1;
}
int numDecodings(string s) {
if(s.length()==0)
return 0;
if(s[0]=='0')
return 0;
vector<int>dp(s.length(),0);
dp[0]=juge1(s[0]);
for(int i=1;i<s.size();i++){
if(i-2>=0)
dp[i]=(juge2(s[i-1],s[i])?dp[i-2]:0)+(juge1(s[i])?dp[i-1]:0);
else
dp[i]=(juge1(s[i])?dp[i-1]:0)+juge2(s[i-1],s[i]);
}
return dp[s.size()-1];
}
};
10、最长递增子序列
300. Longest Increasing Subsequence (Medium)
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
if(nums.empty())
return 0;
vector<int>dp(nums.size(),1);
for(int i=1;i<nums.size();i++){
for(int j=0;j<i;j++){
if(nums[i]>nums[j])
dp[i]=max(dp[i],dp[j]+1);
}
}
int max=0;
for(int i=0;i<nums.size();i++){
if(dp[i]>max)
max=dp[i];
}
return max;
}
};
646. Maximum Length of Pair Chain (Medium)
class Solution {
public:
static bool cmp(vector<int>res1,vector<int>res2){
return res1[0]<res2[0];
}
int findLongestChain(vector<vector<int>>& pairs) {
if(pairs.empty())
return 0;
sort(pairs.begin(),pairs.end(),cmp);//按每一个数组的第一个值排序,因为每个数组的第二个值一定比第一个大
vector<int>dp(pairs.size(),1);
for(int i=1;i<pairs.size();i++){
for(int j=0;j<i;j++){//比较i前面的每个位置上的最长连
if(pairs[i][0]>pairs[j][1])
dp[i]=max(dp[i],dp[j]+1);
}
}
int max=0;
for(int i=0;i<dp.size();i++){
if(dp[i]>max)
max=dp[i];
}
return max;
}
};