代码随想录算法训练营day39|动态规划02

不同路径

图上的动态规划,比较简单,因为只有两个方向可以到达,递归公式易得

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];
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值