leetcode——动态规划

64. 最小路径和

题目:给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。每次只能向下或者向右移动一步

示例:

输入:
[
  [1,3,1],
  [1,5,1],
  [4,2,1]
]
输出: 7
解释: 因为路径 1→3→1→1→1 的总和最小。

--------------------------------------------------------------------------------------------------

思路:定义一维数组dp用于记录起始点到某一点最小距离,数组长度为列长。除了dp[0]的值为0以外,其余的都为最大值Integer.MAX_VALUE。

遍历二维数组:

1)在第一行(i = 0)中,起点到各个点的最短距离的方向就是一直往右走;

2)在第其他行(i > 0)中,由于只能往右或者下方向走,则起点到grid[i][0]的距离只能是一直往下;而对于其他列的点grid[i][j] (j > 0),则需要去比较上一个节点dp[j - 1]与本节点dp[j]之间的大小,所谓的上个节点dp[j - 1]是指左边的节点,而dp[j]]是指上边的节点。

举例说明:对于某一节点(2, 4),dp[3]是指起点到(2,3)的最小距离,而dp[4]是指起点到(1, 4)的最小距离。

    public int minPathSum(int[][] grid) {
      int min = Integer.MAX_VALUE;
       int row = grid.length, col = grid[0].length;
       int[] dp = new int[col];
       for(int i = 1; i < col; i++) {
         dp[i] = Integer.MAX_VALUE;
       }
       dp[0] = 0;
       for(int i = 0; i < row; i++) {
         for(int j = 0; j < col; j++) {
            if(j > 0 && dp[j] > dp[j - 1]) {
              dp[j] = dp[j - 1] + grid[i][j];
            }else {
              dp[j] += grid[i][j];
            }
         }
       }
       return dp[col - 1];
    }

 


96. 不同的二叉搜索树

题目:给定一个整数 n,求以 1 ... n 为节点组成的二叉搜索树有多少种?

示例:

输入: 3
输出: 5
解释:
给定 n = 3, 一共有 5 种不同结构的二叉搜索树:

 

--------------------------------------------------------------------------------------------------

思路:假设n个节点构成的搜索树个数为dp(n),以n为根节点的搜索树个数为f(n)。则可知:

1)dp[n] = f(1) + f(2) + f(3) + ... + f(n)        

2)当i为根节点时,左子树节点为[1, i - 1],总共i - 1个节点;右子树节点为[i + 1, n],总共n - i个节点。因此f(i) = dp(i - 1) * dp(n - i)。

由上面两个式子可得:dp(n) = dp(0) * dp(n - 1) + dp(1) * dp(n - 2) + ... + dp(n - 1) * dp(0)。

举例说明:当n=3时,dp(3) = dp(0) * dp(2) + dp(1) * dp(1) + dp(2) * dp(1)。即式子为dp(i) = dp(j) * (i - j - 1) 【j属于[0, i)】

 

    public int numTrees(int n) {
      int[] dp = new int[n + 1];
      dp[0] = 1;
      dp[1] = 1;
      for(int i = 2; i <= n; i++) {
        for(int j = 0; j < i; j++) {
          dp[i] += dp[j] * dp[i - j - 1];
        }
      }
      return dp[n];
    }

 


152. 乘积最大子数组

题目:给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字)。

示例 1:

输入: [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。
示例 2:

输入: [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。

--------------------------------------------------------------------------------------------------

思路

遍历数组,每个位置的最大连续子数组的最大乘积有三种选择:

(1)选择自己本身nums[i];

(2)选择本身与之前记录的最大值相乘:curMax * nums[i];

(3)选择本身与之前记录的最小值相乘:curMin * nums[i],因为nums[i]可能为负数,与同样可能为负数的curMin 相乘可能得到更大值。

因此我们需要记录两个变量:截止到上一个位置的最大值curMax,最小值curMin。

    public int maxProduct(int[] nums) {
      int len = nums.length;
      if(len == 0)    return 0;
      else if(len == 1) return nums[0];

      int curMax = nums[0], tmp;
      int curMin = nums[0];
      int res = nums[0];

      for(int i = 1; i < len; i++) {
        tmp = curMax;
        curMax = Math.max(Math.max(curMax * nums[i], nums[i]), curMin *  nums[i]);
        curMin = Math.min(Math.min(tmp * nums[i], nums[i]), curMin *  nums[i]);
        res = Math.max(res, curMax);
      }
      return res;
    }

 


198 打家劫舍

一个小偷计划偷窃沿街的房屋。每间房内都藏有一定的现金,但相邻的房屋在同一晚上被小偷闯入,系统会自动报警。

给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下能够偷窃到的最高金额。

示例 1:

输入: [1,2,3,1]
输出: 4
解释: 偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。偷窃到的最高金额 = 1 + 3 = 4 。

--------------------------------------------------------------------------------------------------

思路:定义一个数组dp表示前i个房子中能抢到的最大现金:、

  • 当i = 1时,dp[0] = nums[0];当i = 2时,dp[1] = max(nums[0], nums[1])。
  • 对于i > 2,从两个选择中选最大的:抢第i个房子,并加上dp[i - 2];不抢第i个房子,保持现有最大现金。
    public int rob(int[] nums) {
      int length = nums.length;
      if(length <= 1) return (length == 0) ? 0 : nums[0];
      int[] dp = new int[length];
      dp[0] = nums[0];
      dp[1] = Math.max(nums[0], nums[1]);
      for(int i = 2; i < length; i++) {
        dp[i] = Math.max(dp[i - 2] + nums[i], dp[i - 1]);
      }
      return dp[length - 1];
    }

 


221. 最大正方形

题目:在一个由 0 和 1 组成的二维矩阵内,找到只包含 1 的最大正方形,并返回其面积。

示例:

输入

1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0

输出: 4

--------------------------------------------------------------------------------------------------

思路:假设定义一个数组dp[row][col],它表示以(i, j)为右下角所构成的最大正方形边长。

(1)初始时,即i = 0或j = 0时,dp[i][j] = matrix[i][j] - '0'。在第一行或第一列的元素为右下角时,它们构成的正方形就是他们本身。

(2)对于其他行和列(i,j),若dp[i][j] = 1,则它们的值dp[i][j]依赖于它的上方(i,j - 1),它的左方(i - 1,j),它的左上方(i - 1,j - 1),若这三个值刚好相等,假设为x,那么dp[i][j] = x + 1;若三个值不相等,那么如果把三个正方形合起来,就会缺少某个角落,因此只能选三个中最小的那个,再加上1。

(3)最终得到的最大值maxCnt * maxCnt就是正方形面积。

上面我们定义的是二维数组,同样我们可以改为一维数组dp[col]。每次遍历时,我们用一个变量topLeft来记录dp[j],这样在当matrix[i][j]为1时,topLeft表示的就是左上方或者左边的值。

    public int maximalSquare(char[][] matrix) {
      int row = matrix.length;
      if(row < 1)    return 0;
      int col = matrix[0].length; 

      int[] dp = new int[col];
      int curLastCnt, topLeft = 0;
      int maxCnt = 0;

       for(int i = 0; i < row; i++) {
         for(int j = 0; j < col; j++) {
           if(i == 0 || j == 0 || matrix[i][j] == '0') {
              topLeft = dp[j];
              dp[j] = matrix[i][j] - '0';
           }else {
             curLastCnt = dp[j];    //即上方
             dp[j] = 1 + Math.min(dp[j],  Math.min(topLeft, dp[j - 1]));
             topLeft = curLastCnt;
           }
            maxCnt = Math.max(dp[j], maxCnt);
         }
       }
       return maxCnt * maxCnt;
    }

 


322. 零钱兑换

题目:给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。

示例 1:

输入: coins = [1, 2, 5], amount = 11
输出: 3 
解释: 11 = 5 + 5 + 1
示例 2:

输入: coins = [2], amount = 3
输出: -1

--------------------------------------------------------------------------------------------------

思路:定义数组dp[amount + 1],dp[i]表示凑成金额i 所需的最少硬币个数。遍历所持有的硬币值num,可以得出:

dp[i] = min(dp[i], dp[i - num] + 1

    public int coinChange(int[] coins, int amount) {
      int len = coins.length;
      if(len < 1)   return -1;
      int cnt = 0;
      int[] dp = new int[amount + 1];

      for(int i = 1; i <= amount; i++) {
        dp[i] = amount + 1;
        for(int num : coins) {
          if(num <= i) {         //若当前遍历到的硬币值小于等于 i
            dp[i] = Math.min(dp[i], dp[i - num] + 1);
          }
        }
      }
      return dp[amount] == amount + 1 ? -1 : dp[amount];
    }

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值