LeetCode 63、343、96

102 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第30天,点击查看活动详情

本文包含下面几题:

  1. 给定一个网格和一个机器人,网格中包含障碍物,现在要求机器人从左上角到右下角,问有多少种方法。
  2. 给定一个整数,将其拆分位k个正整数的和,使得这些整数成绩最大化。
  3. 求由n个结点组成的二叉搜索数的数量。

解题思路

LeetCode 63 不同路径II

和之前机器人走路那题一样,但本题多了障碍物,一旦最终终点是障碍物,那直接返回0即可。否则还是和之前推导一样,如果当前位置的左边有障碍物,那只能从上面过来,如果上面有障碍物,那只能从左边过来,如果两边都存在障碍物,那该点就是不可达,可的判断语句如下:

for(int i=1;i<m;i++){
    for(int j=1;j<n;j++){
        if(obstacleGrid[i-1][j]==1&&obstacleGrid[i][j-1]==1){
            dp[i][j] = 0;
        }else  if(obstacleGrid[i-1][j]==1){
            dp[i][j] = dp[i][j-1];
        }else if(obstacleGrid[i][j-1]==1){
            dp[i][j] = dp[i-1][j];
        }else {
            dp[i][j] = dp[i-1][j] + dp[i][j-1];
        }
    }
}

但实际上,当我们到达的点如果是障碍物,那直接continue即可,此时默认值即为0,最终代码如下:

public int uniquePathsWithObstacles(int[][] obstacleGrid) {
    int m = obstacleGrid.length;
    int n = obstacleGrid[0].length;
    if(obstacleGrid[m-1][n-1]==1) return 0;
    int[][] dp = new int[m][n];
    for(int i=0;i<n;i++){
        if(obstacleGrid[0][i]==1) break;
        dp[0][i] = 1;
    }
    for(int i=0;i<m;i++){
        if(obstacleGrid[i][0]==1) break;
        dp[i][0] = 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];
}

LeetCode 343、整数拆分

整数拆分,那拆分后的整数还可以再一次进行拆分,如果能确定每个整数拆分后的乘积最大值,那本题也就容易了,根据此思路可确定最终方法采用动态规划。

我们设定dp[i]代表将整数i拆分后的乘积最大值,对于整数i(由题可知i必然大于等于2,否则将无意义),我们可以将其拆为从1~i-1i的数,假设1~i-1范围的数为j,则最终的最大值为:

dp[i]=max(dp[i],j(ij),jdp[ij])dp[i] = max(dp[i], j*(i-j), j*dp[i-j])

此处之所以不是dp[j]dp[ij]dp[j]*dp[i-j]是因为dp[i]dp[i]代表的是整数i拆解后的最大值,其最终并不一定代表乘积最大,如果使用dp[j]则会丢失j这种情况。

最终可得代码如下:

public int integerBreak(int n) {
    int[] dp = new int[n + 1];
    dp[2] = 1;
    for(int i=3;i<=n;i++){
        for(int j=1;j<=i-1;j++){
            dp[i] = Math.max(dp[i], Math.max(j*(i-j), j*dp[i-j]));
        }
    }
    return dp[n];
}

LeetCode 96、不同的二叉搜索树

通过观察我们可以发现,如果一个结点当根节点,那么其左右子树的数量是确定的,并且左右子树组成的二叉搜索树和数值无关,可得出当j作为根节点,那可能组成的二叉树共有dp[j1]dp[ij]dp[j-1]*dp[i-j]个,这一点可在n=3时验证,最终可得代码(注意注释):

public int numTrees(int n) {
    int[] dp = new int[n + 1];
    dp[0] = 1;
    dp[1] = 1;
    for(int i=2;i<n+1;i++){
        for(int j=1;j<=i;j++){
            // 对于第i个节点,需要考虑1作为根节点直到i作为根节点的情况,所以需要累加
            // 一共i个节点,对于根节点j时,左子树的节点个数为j-1,右子树的节点个数为i-j
            dp[i] += dp[j-1] * dp[i-j];
        }
    }
    return dp[n];
}