Leetcode刷题笔记Day16:动态规划Ⅰ

89 阅读2分钟

不同路径

  • 力扣题目链接
  • 动态规划问题最重要的事情就是状态转移方程!
  • 先来试下暴力深搜(超时警告⚠
int count=0;
void dfs(int x, int y, int m, int n){
    if(x==m && y==n) {count++; return;}
    if(x>m || y>n) return;
    dfs(x+1, y, m, n);
    dfs(x, y+1, m, n);
}
int uniquePaths(int m, int n) {
    dfs(1, 1, m, n);
    return count;
}
  • 上面解法的问题就是递归可能爆栈了,事实上,只需要dp避免诸多重复计算
#define N 100
int dp[N][N];
int uniquePaths(int m, 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];
}
  • 空间优化版:
#define N 100
int dp[N];
int uniquePaths(int m, int 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];
}
  • 组合数版本:C(m+n-2, m-1)
int uniquePaths(int m, int n) {
    if(m==1||n==1) return 1; 
    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;
}

不同路径Ⅱ

  • 力扣题目链接
  • 这道题目开始有障碍物了,有点小区别但不多,碰到障碍物之后保持原来状态
#define N 100
int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
    if(obstacleGrid[0][0]==1) return 0;
    int m=obstacleGrid.size(), n=obstacleGrid[0].size();
    int dp[N]={0};
    for (int j=0; j<n && obstacleGrid[0][j]==0; j++)
        dp[j]=1;
​
    for (int i=1; i<m; i++)
        for (int j=0; j<n; j++)
            if (obstacleGrid[i][j]==1) dp[j] = 0;
    else if (j!=0) dp[j]+=dp[j-1];
​
    return dp[n-1];
}

整数拆分

  • 力扣题目链接
  • 这道题目的dp方程需要推导明白,事实上,dp[0]和dp[1]不是必须的
#define N 59
int dp[N]={0};
int integerBreak(int n) {
    dp[2]=1;
    for(int i=3; i<=n; i++)
        for(int j=1; j<i-1; j++)    // 事实上,这里≤i/2就好
            dp[i]=max(dp[i], max((i-j)*j, dp[i-j]*j));
    return dp[n];
}
  • 贪心法:不断取3即可,数学不论证

不同的二叉搜索树

  • 力扣题目链接
  • 这道题目看似有些抽象,但是我们通过观察n=3的情况
  • 不难看出dp[3]=dp[2]*dp[0]+dp[1]*dp[1]+dp[0]*dp[2]
#define N 20
int dp[N]={1};
int numTrees(int n) {
    for(int i=1; i<=n; i++)
        for(int j=1; j<=i; j++)
            dp[i]+=dp[j-1]*dp[i-j];
    return dp[n];
}
  • 综合以上题目,dp实际上就是记忆化搜索,用空间换时间!

参考资料

[1] 代码随想录

[2] Leetcode题解