三个无重叠子数组的最大和

689. 三个无重叠子数组的最大和

给定数组 nums 由正整数组成,找到三个互不重叠的子数组的最大和。

每个子数组的长度为k,我们要使这3*k个项的和最大化。

返回每个区间起始索引的列表(索引从 0 开始)。如果有多个结果,返回字典序最小的一个。

示例
输入: [1,2,1,2,6,7,5,1], 2
输出: [0, 3, 5]
解释: 子数组 [1, 2], [2, 6], [7, 5] 对应的起始索引为 [0, 3, 5]。
我们也可以取 [2, 1], 但是结果 [1, 3, 5] 在字典序上更大。
注意:
  • nums.length的范围在[1, 20000]之间。
  • nums[i]的范围在[1, 65535]之间。
  • k的范围在[1, floor(nums.length / 3)]之间。

题解

  1. 假设nums的长度为len,求出nums数组所有连续k个子序列的和的数组sums,已知nums的长度为len,sums的长度则为len-k+1,用t表示。
  2. 那么题目就可以转化为求sums数组,选3个数,这三个数必须满足,相邻间隔不小于k,当和最大时,脚标最小的三个数。
  3. 定义dp数组,dp[i][j]表示的是从[0, i]的范围内,选择j个数得到的最大和(j个数的相邻间隔为k)
  4. 定义path数组,path[i][j]表示的是当得到dp[i][j]的时候,选择的最后一个数的脚标
定义dp数组的状态转移方程

sums[i]分为选择 和 不选择
当选择sums[i] 时 ,dp[i][j] = dp[i-k][j-1] + sums[i];
当不选择sums[i] 时, dp[i][j] = dp[i-1][j];
最终求最大值: dp[i][j] = max{dp[i-k][j-1] + sums[i], dp[i-1][j]}

初始化dp数组需要注意的是
要在sums数组中选择j个数,并且满足相邻的数间隔不小于k,那么sums的长度必须满足t > (j-1)*k,由于i代表的是脚标,所以只有在i >= (j-1)*k的时候,dp数组才有意义
边界问题:当k=1时,比较特殊,可以单独初始化,因为k=1时,哪怕sums只有一个数,也可以选择,但是如果k>1时,比如k=2,当sums有2个数的时候,其实是无法选择的。

以 [1,2,1,2,6,7,5,1], 2 输入为例,
sums数组为:
[3, 3, 3, 8, 13, 12, 6]
得到的dp数组和path数组分别为:

dp 0 1 2 3
0 0 3 0 0
1 0 3 0 0
2 0 3 6 0
3 0 8 11 0
4 0 13 16 19
5 0 13 20 23
6 0 13 20 23
path 0 1 2 3
0 0 0 0 0
1 0 0 0 0
2 0 0 2 0
3 0 3 3 0
4 0 4 4 4
5 0 4 5 5
6 0 4 5 5

最后得到path,那么path[t-1][3]就是从[0, t-1]的范围内,选择3个数和最大时,最后一个数的脚标,用res[2]来表示,那么前一个的脚标一定是[0, res[2]-k]的范围内,选择2个数和最大时,最后一个数的脚标,依次类推,得到第一个脚标。

代码如下:
class Solution {
    public int[] maxSumOfThreeSubarrays(int[] nums, int k) {
        int len = nums.length;
        int t = len-k+1;
        int[] sums = new int[t];
        int[] res = new int[3];
        for(int i=0; i dp[i-1][1] ? i : path[i-1][1];
        }
        for(int i=0;i= (j-1)*k){
                    int select = dp[i-k][j-1] + sums[i];
                    int notSelect = dp[i-1][j];
                    dp[i][j] = Math.max(select, notSelect);
                    path[i][j] = path[i-1][j];
                    if(select > notSelect){
                        path[i][j] = i;
                    }
                }
            }
        }

        res[2] = path[t-1][3];
        res[1] = path[res[2]-k][2];
        res[0] = path[res[1]-k][1];
        return res;
    }

该题还可以引申一下,把找3个子数组,引申到找n个子数组:

class Solution {
    public int[] maxSumOfThreeSubarrays(int[] nums, int k) {
        return maxSumOfNSubarrays(nums,k,3);
    }

    public int[] maxSumOfNSubarrays(int[] nums, int k, int n){
        int len = nums.length;
        int t = len-k+1;
        int[] sums = new int[t];
        int[] res = new int[n];
        for(int i=0; i dp[i-1][1] ? i : path[i-1][1];
        }
        for(int i=0;i= (j-1)*k){
                    int select = dp[i-k][j-1] + sums[i];
                    int notSelect = dp[i-1][j];
                    dp[i][j] = Math.max(select, notSelect);
                    path[i][j] = path[i-1][j];
                    if(select > notSelect){
                        path[i][j] = i;
                    }
                }
            }
        }

        int row = t-1;
        int col = n;
        res[n-1] = path[row][col--];
        for(int i=n-2; i>=0; i--){
            res[i] = path[res[i+1]-k][col--];
        }
        return res;
    }
}

你可能感兴趣的:(算法,leetcode,动态规划,算法,java)