不同路径
图上的动态规划,比较简单,因为只有两个方向可以到达,递归公式易得
class Solution {
public:
int uniquePaths(int m, int n) {
vector<vector<int>> dp(m, vector<int>(n, 0));
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];
}
};
可简化为一维的
class Solution {
public:
int uniquePaths(int m, int n) {
vector<int> dp(n);
for(int i=0;i<n;i++) dp[i]=1;
for(int j=1;j<m;j++)
for(int i=1;i<n;i++)
dp[i]+=dp[i-1];
return dp[n-1];//相当于只维护一行的就行了
}
};
如果采用数论的方法,要及时除分母,否则会两个int相乘溢出。不能把算式的分子都算出来,分母都算出来再做除法。
class Solution {
public:
int uniquePaths(int m, int n) {
int numerator = 1, denominator = 1;
int count = m - 1;
int t = m + n - 2;
while (count--) numerator *= (t--); // 计算分子,此时分子就会溢出
for (int i = 1; i <= m - 1; i++) denominator *= i; // 计算分母
return numerator / denominator;
}
};
不同路径||
有障碍物,那么就将对应的dp保持初始值0就好了,表示没有路径能到达障碍物所在的位置
因为递推公式变化,所以初始化代码也要跟着变化
class Solution {
public:
int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
int m=obstacleGrid.size();//注意外层这个vector才是纵向的
int n=obstacleGrid[0].size();//这个才是横向的
if(obstacleGrid[m-1][n-1]==1||obstacleGrid[0][0]==1)
return 0;//如果在起点或者终点出现障碍,直接返回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;
for(int i=1;i<m;i++){
for(int j=1;j<n;j++){
if(obstacleGrid[i][j]==1) continue;
dp[i][j]=dp[i-1][j]+dp[i][j-1];
}
}
return dp[m-1][n-1];
}
};
整数拆分
dp[i]:分拆数字i,可以得到的最大乘积为dp[i]。递推公式,dp[i] = max(dp[i], max((i - j) ×, dp[i - j] 乘j));理解即是j * (i - j) 是单纯的把整数拆分为两个数相乘,而j *dp[i - j]是拆分成两个以及两个以上的个数相乘。
其实这样分解有一点贪心的策略在
优化主要针对循环次数,肯定是拆分的更多,数更相近乘积更大,所以可以把j<i改为j<=i/2
class Solution {
public:
int integerBreak(int n) {
vector<int> dp(n+1);
dp[2]=1;
for(int i=3;i<=n;++i){
for(int j=1;j<=i/2;j++){
dp[i]=max(dp[i],max((i-j)*j,dp[i-j]*j));
}
}
return dp[n];
}
};
不同的二叉搜索树
二叉树的分解与动态规划的思想结合
class Solution {
public:
int numTrees(int n) {
vector<int> dp(n+1);
dp[0]=1;
for(int i=1;i<=n;++i){//dp[i] += dp[以j为头结点左子树节点数量] * dp[以j为头结点右子树节点数量]
for(int j=1;j<=i;j++){//j相当于是头结点的元素,从1遍历到i为止。
dp[i]+=dp[j-1]*dp[i-j];
}
}
return dp[n];
}
};