LintCode43:Maximum Subarray III(求能得到最大和的分组,输出最大和)

动态规划比较难想,但写出来的代码确实简洁,而且处理的逻辑很清晰,不过动态规划很多时候并不是最优解,这里只作为一种理解和示意

public class MaxSubArray {

  public static int maxSubArray(int[] nums, int k) {
    // 动态规划最重要的就是找到递推关系
    // 递推的目标是遍历处理完所有元素,处理下一个元素时能够利用到之前的结果,最终得出结果
    // 这里我们需要找的是把所有元素分成k组,和把nums.length-1个元素分成k组的关系
    // 很明显这个关系就是求Math.max
    // 我们在遍历时需要注意几点,第一,起始点肯定是跟组数相同的长度,少于需分的组数i的nums元素没法分成i组
    // 第二,起始点确定后能说明啥?是不是就能确定截至起始点的长度j,就已经能分成i个组了!
    // 那如何确定下一个元素是否要被安排进已分好的组呢?
    // 我们需要知道上一元素一定被包含的i个数组的最大和
    // 与到上个元素为止所有已分好的i-1个数组(可能不包含上个元素,这要注意)实际的最大和谁大谁小
    // 可见我们需要一个标识,能够代表一定包含上一个元素j的最大和(不一定是实际最大)

    int len = nums.length;
    // 如果数组长度小于要分的组数,不够分,直接返回0
    if (len < k) {
      return 0;
    }

    // max[i][j]表示数组nums中包括第j个元素及以前的元素分成i个组,能得到的最大和
    int[][] realMax = new int[k + 1][len + 1];
    // 在遇到一个新元素j时,第j个元素可能不被包含在最大和分组中,所以需要一个能够包含第j个元素的最大和
    int[][] virtualMax = new int[k + 1][len + 1];
    for (int i = 1; i <= k; i++) {
      virtualMax[i][i - 1] = Integer.MIN_VALUE;
      for (int j = i; j <= len; j++) {
        // 这一步就是一定要包含第j个元素进来(virtualMax就是代表一定包含所求元素的最大和),
        // 所以这不一定是最大和
        virtualMax[i][j] = Math.max(virtualMax[i][j - 1], realMax[i - 1][j - 1]) + nums[j - 1];
        // i和j 重合时virtualMax代表的最大和就是最大和
        // 这里也可以看出,realMax不一定包含第j个元素
        realMax[i][j] = (i == j ? virtualMax[i][j] : Math.max(virtualMax[i][j], realMax[i][j - 1]));
      }
    }

    return realMax[k][len];
  }

}

你可能感兴趣的:(算法)