blog-web/source/_posts/算法/动态规划(1).md
2018-08-31 23:50:57 +08:00

4.0 KiB
Raw Blame History

title date tags categories
动态规划(1) 2018-8-27 02:17:07
算法
动态规划
算法

楔子

最大子序和 ( leetcode题目 ) 给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

示例: 输入: [-2,1,-3,4,-1,2,1,-5,4], 输出: 6 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

题目只需要求最大的和是多少, 而不需要知道这个子数组的起止位置 首先需要明确的是, 如果这个子数组的边缘位置(第一个或最后一个)元素是一个负数 那么这个子数组的和肯定不是最大的, 因为舍掉这个元素, 之后的和肯定会更大, 负数是会让总和减小的 比如[-3,4,-1,2] 这个子数组肯定不是最大, 因为把-3舍掉会更大

更进一步, 如果这个子数组边缘位置连续多个数的和是负数, 那么这多个数也可以舍掉, 让总和更大 比如[1,-3,4,-1,2] 这个子数组肯定不是最大, 因为把1,-3舍掉, 可以让总和更大

以示例当中给出的数组进行推演 [-2,1,-3,4,-1,2,1,-5,4] ( 下面的每个序号代表推进到第几个元素 )

  1. 总和初始值是0 , 先加第一个数此时总和是-2
  2. 前面的总和-2, 加上它会使总和 变小, 所以不加, 总和 归零, 然后加1, 此时总和是1
  3. 前面的总和1, 加上它会使总和 变大, 所以加上, 此时总和是-2
  4. 前面的总和-2, 加上它会使总和 变小, 所以不加, 总和 归零, 然后加4, 此时总和是4
  5. 前面的总和4, 加上它会使总和 变大, 所以加上, 此时总和是3

..... 每一步是否舍弃前面总和的判断条件就是前面的总和是正还是负, 正数则加, 负数则舍弃 依次进行下去, 从每一步得到的总和里面找出最大的就可以了 每一步的总和分别是-2 1 -2 4 3 5 6 1 5 显然最大的是6

代码实现

class Solution {
  public int maxSubArray(int[] nums) {
    if(nums.length == 0) { //特殊情况判断
      return 0;
    }
    int sum = nums[0], maxSum = nums[0];
    for(int i=1 ; i<nums.length ; i++) {
      sum = (sum < 0 ? nums[i] : sum + nums[i]);
      if(sum > maxSum) {
        maxSum = sum;
      }
    }
    return maxSum;
  }
}

空间复杂度是O(1), 因为使用了常数个变量, 没有开辟长度为n的新数组 时间复杂度是O(n), 因为要逐个遍历传入的数组当中的元素

不同路径问题

一个机器人位于一个 m x n 网格的左上角 起始点在下图中标记为“Start” 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角在下图中标记为“Finish” 问总共有多少条不同的路径? 不同路径问题

解决方式

  1. 首先最左上角的格子的到达方式肯定只有1种, 因为必须要从这个格子开始走
  2. 在每个格子当中只能向右或者向下移动, 所以每个对于每个格子来说, 到达这个格子的时候, 只能从左侧或者上方到达
  3. 左边缘的格子无法从左侧到达, 上边缘的格子无法从上方到达

所以对于每个格子来说, 到达这个格子的路径的数量 = 到达左侧格子的数量 + 到达上方格子的数量 左边缘的格子前者为0, 上边缘的格子后者为0

根据这个原则, 就可以把到达每个格子的路径数量递推出来了

代码实现

public int uniquePaths(int m, int n) {
  if(m<=0 || n<=0) {
    return 0;
  }
  int[][] nums = new int[m+1][n+1];
  nums[1][1] = 1;
  for(int i=1 ; i<=m ; i++) {
    for(int j=1 ; j<=n ; j++) {
      if(i==1 && j==1) {
        continue;
      }
      nums[i][j] = nums[i-1][j] + nums[i][j-1];
    }
  }
  return nums[m][n];
}

整体思路就是创建一个整数二维数组, m+1和n+1是为了留出第一行和第一列数值都是0 方便进行计算, 当然这个也不是必须的, 在循环当中判断也可以, 但是不影响时间和空间复杂度