// 动规五部曲
// 1.确定dp数组以及下标的含义
// 2.确定递推公式
// 3.dp数组如何初始化
// 4.确定遍历顺序
// 5.举例推到dp数组
// class Solution {
// public:
// int fib(int n) {
// if(n<=1) return n;
// vector<int> dp(n+1);
// dp[0]=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<2) return n;
int dp[2];
dp[0]=0;
dp[1]=1;
for(int i=2;i<=n;i++){
int tmp=dp[0]+dp[1];
dp[0]=dp[1];
dp[1]=tmp;
}
return dp[1];
}
};
// 递归
// class Solution {
// public:
// int fib(int n) {
// if(n==0){
// return 0;
// }
// else if(n==1){
// return 1;
// }
// else{
// return fib(n-1)+fib(n-2);
// }
// }
// };
class Solution {
public:
int climbStairs(int n) {
// 先推理爬上一阶有1种方法,爬上二阶有2种方法。
// 那么爬上三阶,就是从一阶一步爬两个台阶上三阶或从二阶一步一个台阶上三阶,有1+2=3种方法
// 所以
// dp[i]表示爬到n个台阶有多少种方法
// 爬上i-1阶有dp[i-1]种方法,再一步一个台阶就是dp[i];爬上i-2阶有dp[i-2]种方法,再一步两个台阶就是dp[i]
// dp[i]=dp[i-1]+dp[i-2]
vector<int> dp(n+1);
for(int i=1;i<=n;i++){
if(i<3) dp[i]=i;
else{
dp[i]=dp[i-1]+dp[i-2];
}
}
return dp[n];
}
};
// 优化空间复杂度
// 因为只需要维护两个数值即可,不需要记录整个序列
// class Solution {
// public:
// int climbStairs(int n) {
// if(n<=1) return n;
// int dp[2];
// // 这里代码随想录错误,应该是dp[1]和dp[2],从3开始
// dp[0]=1;//存放dp[i-2]
// dp[1]=2;//存放dp[i-1]
// for(int i=3;i<=n;i++){
// int sum=dp[0]+dp[1];
// dp[0]=dp[1];
// dp[1]=sum;
// }
// return dp[1];//返回dp[i]
// }
// };
// class Solution {
// public:
// int minCostClimbingStairs(vector<int>& cost) {
// // dp[i]为爬到第i个台阶的最低费用
// // dp[0]=cost[0];dp[1]=cost[1]
// // dp[2]=min(dp[0]+cost[2],dp[1]+cost[2])
// // 以此类推,dp[i]=min(dp[i-2]+cost[i],dp[i-1]+cost[i])
// // 上面属于是理解错误了,题目意思是爬到cost.size()而不是cost.size()-1
// // dp[0]=0,dp[1]=0;再从第0阶/第1阶往上跳的时候要支付cost[0]/cost[1]的费用
// // dp[i]=min(dp[i-2]+cost[i-2],dp[i-1]+cost[i-1])
// // 这里dp[]要比cost多一个,返回的也是dp[]最后一个
// vector<int> dp(cost.size()+1);
// for(int i=0;i<cost.size()+1;i++){
// if(i<2) dp[i]=0;
// else{
// dp[i]=min(dp[i-2]+cost[i-2],dp[i-1]+cost[i-1]);
// }
// }
// return dp[cost.size()];
// }
// };
// 优化空间复杂度
class Solution {
public:
int minCostClimbingStairs(vector<int>& cost) {
int dp[2];
dp[0]=0;
dp[1]=0;
for(int i=2;i<cost.size()+1;i++){
int tmp=min(dp[0]+cost[i-2],dp[1]+cost[i-1]);
dp[0]=dp[1];
dp[1]=tmp;
}
return dp[1];
}
};
// 动态规划
// 通过分析具体案例发现dp[i][0]=dp[0][j]=1
// 继续分析发现dp[i][j]=dp[i][j-1]+dp[i-1][j];
// 时间复杂度:O(m × n);空间复杂度:O(n)
class Solution {
public:
int uniquePaths(int m, int n) {
vector<vector<int>> dp(m,vector<int>(n));
// 这里在赋值dp数组时,要注意要先将dp[i][0]=dp[0][j]=1赋值才可以继续计算,因为其他位置都依赖于他们,否则是不能计算的
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][j-1]+dp[i-1][j];
}
}
return dp[m-1][n-1];
}
};
// 要从[0,0]走到[m,n]一定要有m-1步向下,n-1步向右,一共要走m+n-1步
// 那么可以转换为排列组合问题,什么时候向右走什么时候向下走
// 组合问题不应该用回溯吗?
// class Solution {
// public:
// int uniquePaths(int m, int n) {
// long long numerator=1;
// int denominator=m-1;
// int count=m-1;
// int t=m+n-2;
// // 说实话,根本看不懂
// while(count--){
// numerator*=(t--);
// while(denominator!=0&&numerator%denominator==0){
// numerator/=denominator;
// denominator--;
// }
// }
// return numerator;
// }
// };
// 时间复杂度:O(n × m),n、m 分别为obstacleGrid 长度和宽度;空间复杂度:O(n × m)
class Solution {
public:
int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
int m=obstacleGrid.size();
int n=obstacleGrid[0].size();
// 初始化dp数组为全0
// 初始化dp[i][0]时,当前面没有障碍物则dp[i][0]=1,一旦前面有障碍物则dp[i][0]=0;dp[0][j]同理
vector<vector<int>> dp(m,vector<int>(n,0));
for(int i=0;i<m&&obstacleGrid[i][0]==0;i++) dp[i][0]=1;
for(int j=0;j<n&&obstacleGrid[0][j]==0;j++) dp[0][j]=1;
// dp[i][j]仍与上一题相同,只不过遇到障碍物时dp[i][j]=0
for(int i=1;i<m;i++){
for(int j=1;j<n;j++){
if(obstacleGrid[i][j]==0) dp[i][j]=dp[i][j-1]+dp[i-1][j];
else continue;
}
}
return dp[m-1][n-1];
}
};
// 空间复杂度优化
// 时间复杂度:O(n × m),n、m 分别为obstacleGrid 长度和宽度;空间复杂度:O(m)
// class Solution {
// public:
// int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
// if(obstacleGrid[0][0]==1) return 0;
// // 按行存储dp
// vector<int> dp(obstacleGrid[0].size());
// for(int j=0;j<dp.size();j++){
// if(obstacleGrid[0][j]==1) dp[j]=0;
// else if(j==0) dp[j]=1;
// else dp[j]=dp[j-1];
// }
// for(int i=1;i<obstacleGrid.size();i++){
// for(int j=0;j<dp.size();j++){
// if(obstacleGrid[i][j]==1) dp[j]=0;
// // 这里dp[j]存储的是dp[i-1][j],dp[j-1]存储的是dp[i][j-1]
// else if(j!=0) dp[j]=dp[j]+dp[j-1];
// }
// }
// return dp.back();
// }
// };