class Solution {
public:
int fib(int n) {
if(n<=1){
return n;
}
return fib(n-1)+fib(n-2);
}
};
dp[i]含义:第i个斐波那契数的数值
递推公式:dp[i]=dp[i-1]+dp[i-2]
初始化:dp[0]=0,dp[1]=1
遍历顺序:从前向后依次遍历来完成填表
class Solution {
public:
int fib(int n) {
if(n<=1){
return n;
}
vectordp(n+1,0);
dp[1]=1;
for(int i=2;i<=n;i++){
dp[i]=dp[i-1]+dp[i-2];
}
return dp[n];
}
};
class Solution {
public:
int fib(int n) {
if(n<=1){
return n;
}
//用a表示i-2的值,b表示i-1的值,不断更新a和b
int a=0;
int b=1;
for(int i=2;i<=n;i++){
int c=a+b;
a=b;
b=c;
}
return b;
}
};
class Solution {
public:
int climbStairs(int n) {
if(n<=1){
return 1;
}
return climbStairs(n-1)+climbStairs(n-2);
}
};
class Solution {
public:
int process(int n,vector&res){
if(res[n]!=-1){
return res[n];
}
if(n<=1){
return 1;
}
res[n]=process(n-1,res)+process(n-2,res);
return res[n];
}
int climbStairs(int n) {
if(n<=1){
return 1;
}
vectorres(n+1,-1);
return process(n-1,res)+process(n-2,res);
}
};
dp[i]含义:爬第i个楼层的方法数
递推公式:dp[i]=dp[i-1]+dp[i-2]
初始化:dp[0]=1,dp[1]=1
遍历顺序:从前向后依次遍历来完成填表
class Solution {
public:
int climbStairs(int n) {
if(n<=1){
return 1;
}
vectordp(n+1,1);
for(int i=2;i<=n;i++){
dp[i]=dp[i-1]+dp[i-2];
}
return dp[n];
}
};
class Solution {
public:
int climbStairs(int n) {
if(n<=1){
return 1;
}
int a=1;int b=1;
for(int i=2;i<=n;i++){
int c=a+b;
a=b;
b=c;
}
return b;
}
};
class Solution {
public:
//暴力递归的过程
int process(vector&cost,int index){
if(index==0||index==1){
return 0;
}
//该楼梯的最小费用
return min(process(cost,index-1)+cost[index-1],process(cost,index-2)+cost[index-2]);
}
int minCostClimbingStairs(vector& cost) {
if(cost.size()<=1){
return 1;
}
return process(cost,cost.size());
}
};
dp[i]含义:爬第i个楼层的最小费用
递推公式:dp[i]=min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2])
初始化:dp[0]=0,dp[1]=0
遍历顺序:从前向后依次遍历来完成填表
class Solution {
public:
int minCostClimbingStairs(vector& cost) {
vectordp(cost.size()+1,0);
for(int i=2;i<=cost.size();i++){
dp[i]=min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]);
}
return dp[cost.size()];
}
};
class Solution {
public:
int minCostClimbingStairs(vector& cost) {
int a=0;int b=0;
for(int i=2;i<=cost.size();i++){
int c=min(a+cost[i-1],b+cost[i-2]);
b=a;
a=c;
}
return a;
}
};
class Solution {
public:
int process(int row,int col){
//当到达最上方和最左方时只有一种方法,此时返回
if(row==1){
return 1;
}
if(col==1){
return 1;
}
//当前位置可由该位置的上方和该位置的左方到达
return process(row-1,col)+process(row,col-1);
}
int uniquePaths(int m, int n) {
return process(m,n);
}
};
dp[i][j]含义:到达i行j列位置的方法数
递推公式:dp[i][j]=dp[i-1][j]+dp[i][j-1]
初始化:dp[i][0]=1,dp[0][j]=1(第一行位置全为1,第一列位置全为1)
遍历顺序:从前向后依次遍历来完成填表
class Solution {
public:
int uniquePaths(int m, int n) {
vector>dp(m,vector(n,0));
for(int i=0;i
class Solution {
public:
int uniquePaths(int m, int n) {
vectordp(n,1);
for(int i=1;i
class Solution {
public:
int process(vector>& obstacleGrid,vector>&start,int row,int col){
if(obstacleGrid[row-1][col-1]){
return 0;
}
if(row==1){
return start[0][col-1];
}
if(col==1){
return start[1][row-1];
}
return process(obstacleGrid,start,row-1,col)+process(obstacleGrid,start,row,col-1);
}
int uniquePathsWithObstacles(vector>& obstacleGrid) {
//打一张表来记录我们后续递归到哪些位置返回1,哪些位置返回0
//我们只需要记录最左边和最上边的位置,如果用m*n二维数组的话浪费空间
//折叠一下表,用一张2*max(m,n)的表来代替即可
int m=obstacleGrid.size();
int n=obstacleGrid[0].size();
vector>start(2,vector(max(m,n),0));
for(int i=0;i
class Solution {
public:
int process(vector>& obstacleGrid,vector>&start,
vector>&res,int row,int col){
if(obstacleGrid[row-1][col-1]){
return 0;
}
if(res[row-1][col-1]!=-1){
return res[row-1][col-1];
}
if(row==1){
return start[0][col-1];
}
if(col==1){
return start[1][row-1];
}
res[row-1][col-1]=process(obstacleGrid,start,res,row-1,col)+process(obstacleGrid, start,res,row, col-1);
return res[row-1][col-1];
}
int uniquePathsWithObstacles(vector>& obstacleGrid) {
//打一张表start来记录我们后续递归到哪些位置返回1,哪些位置返回0
//我们只需要记录最左边和最上边的位置,如果用m*n二维数组的话浪费空间
//折叠一下表,用一张2*max(m,n)的表来代替即可
//暴力递归超时,学会记忆化搜索,再打一张表memory来记录一下已知的结果
vector>memory(obstacleGrid.size(),vector(obstacleGrid[0].size(),-1));
int m=obstacleGrid.size();
int n=obstacleGrid[0].size();
vector>start(2,vector(max(m,n),0));
for(int i=0;i
lass Solution {
public:
int uniquePathsWithObstacles(vector>& obstacleGrid) {
vector>dp(obstacleGrid.size(),vector(obstacleGrid[0].size(),0));
for(int i=0;i
class Solution {
public:
int uniquePathsWithObstacles(vector>& obstacleGrid) {
vectordp(obstacleGrid[0].size(),0);
for(int i=0;i=1&&!obstacleGrid[i][j-1]){
dp[j]+=dp[j-1];
}
}
}
return dp[obstacleGrid[0].size()-1];
}
};
后续我将不再提供暴力递归的方法,其时间复杂度过高,不如打一张表记录相同子问题的结果
class Solution {
public:
int process(int n,vector&res){
if(res[n]){
return res[n];
}
for(int i=1;i<=n/2;i++){
res[n]=max(res[n],max(process(n-i,res)*i,(n-i)*i));
}
return res[n];
}
int integerBreak(int n) {
vectorres(n+1,0);
return process(n,res);
}
};
这里有人将其理解为背包问题,即选择数装满一个容量为n的背包,这些数的最大乘积是多少,当然这里的数可以重复选取,所以是完全背包问题,我个人认为这题理解成背包反而增加了难度
class Solution {
public:
int integerBreak(int n) {
//dp[i]:将i拆分成k个正整数的最大乘积
vectordp(n+1,0);
dp[1]=0;dp[2]=1;
for(int i=3;i<=n;i++){
for(int j=1;j
将每个节点作为根节点递归并打一张表来记录一下结果
class Solution {
public:
int process(int n,vector&res){
if(res[n]){
return res[n];
}
for(int i=1;i<=n;i++){
res[n]+=process(i-1,res)*process(n-i,res);
}
return res[n];
}
int numTrees(int n) {
vectorres(n+1,0);
res[0]=1;
res[1]=1;
return process(n,res);
}
};
dp[i]:从1到i这些节点所组成二叉搜索树的种数
class Solution {
public:
int numTrees(int n) {
vectordp(n+1,0);
dp[0]=1;
dp[1]=1;
for(int i=2;i<=n;i++){
for(int j=1;j<=i;j++){
dp[i]+=dp[j-1]*dp[i-j];
}
}
return dp[n];
}
};
这些题目作为想要尝试动态规划的新手来说是非常适合的,不涉及一些比较难的动态规划问题,按照顺序理解并掌握这些动态规划的问题,就可以算是动态规划入门了