[LeetCode]动态规划解分割数组I[Red Fox]

75 篇文章 0 订阅

欢迎阅读、点赞、转发、订阅,你的举手之间,我的动力源泉。

nature-3879646_960_720.jpg

写本题,源自看到的官方的一句描述"「将数组分割为 m 段,求……」是动态规划题目常见的问法。",让我想起了之前写的1043. 分隔数组以得到最大和,早期写的题解,风格更像一种草稿,但还是有很多扣友鼓励似的阅读点赞,感谢感谢,笔芯~

image-20200728082139072.png

定义状态

d p [ i ] [ j ] dp[i][j] dp[i][j]表示前 i i i个数即 n u m s [ 0... i − 1 ] nums[0...i-1] nums[0...i1]之间的数被分成 j j j段,所组成的 j j j个子数组各自和的最大值中的最小值

转移方程

image-20200728085040860.png

思考动态规划的类的问题,有点像道家的道生一,一生二,二生三,三生万物,从特殊情况到一般情况,推演归纳

回到本题也是如此,目标是求 d p [ i ] [ j ] dp[i][j] dp[i][j],再读一遍此定义:表示前 i i i个数即 n u m s [ 0... i − 1 ] nums[0...i-1] nums[0...i1]之间的数被分成 j j j段,所组成的 j j j个子数组各自和的最大值中的最小值,我们能不能求其在这 n u m s [ 0... i − 1 ] nums[0...i-1] nums[0...i1]中试着用剪刀,减出来两端,分成两部分来考虑?

  • 第二段:可以想象成第 j j j段,如图上的 n u m s [ k , k + 1 , . . . . i − 1 ] nums[k,k+1,....i-1] nums[k,k+1,....i1],因为我们取的是前 i i i个数,数组下标从 0 0 0开始的,这部分的和很显然就是 s u m ( n u m s [ k , k + 1 , . . . i − 1 ] ) sum(nums[k,k+1,...i-1]) sum(nums[k,k+1,...i1])

  • 第一段:可以想象成前 j − 1 j-1 j1段,那这一段呢?既然是 j − 1 j-1 j1段,其实和 j j j段没什么大的区别,可以转化成 d p dp dp也就是 d p [ k ] [ j − 1 ] dp[k][j-1] dp[k][j1],表示前 k k k个数即 n u m s [ 0... k − 1 ] nums[0...k-1] nums[0...k1]之间的数被分成 j − 1 j-1 j1段,所组成的 j − 1 j-1 j1个子数组各自和的最大值的最小值

  • 上面两端的最小值便是 d p [ i ] [ j ] dp[i][j] dp[i][j]的结果,即 d p [ i ] [ j ] dp[i][j] dp[i][j]= m a x max max( d p [ k ] [ j − 1 ] dp[k][j-1] dp[k][j1], s u m [ k . . . i − 1 ] sum[k...i-1] sum[k...i1]),然后这只是其中随便剪的一刀,也就是 k k k有很多种可能, k k k的范围可以从 0 0 0取到 i − 1 i-1 i1,超过 i − 1 i-1 i1没有意义,如果 k k k> i − 1 i-1 i1,表示前 k k k个数,但是目标只考虑到前 i i i个数

  • 最终的转移方程:

    d p [ i ] [ j ] dp[i][j] dp[i][j]= m i n min min[ m a x max max( d p [ k ] [ j − 1 ] , s u m [ k . . . i − 1 ] dp[k][j-1],sum[k...i-1] dp[k][j1],sum[k...i1])] ,其中 0=< k k k<= i − 1 i-1 i1

边界

  • d p [ 0 ] [ 0 ] dp[0][0] dp[0][0]表示前0个数被分成0段,这在逻辑上是讲不通的,如何出现这种场景呢,也就是整个 n u m s [ 0... i − 1 ] nums[0...i-1] nums[0...i1]被分成 j = 1 j=1 j=1段,那么 d p [ k ] [ j − 1 ] dp[k][j-1] dp[k][j1]就变成了 d p [ 0 ] [ 0 ] dp[0][0] dp[0][0],取其与 s u m [ k . . . i − 1 ] sum[k...i-1] sum[k...i1]的最大值,只要让 d p [ 0 ] [ 0 ] dp[0][0] dp[0][0]=0就不会影响最后的结果
  • j j j> i i i,没有意义,因为,前 i i i个数字不可能被分成超过 i i i段,因为一个数字只能最多被分成一组,每个数组单独一组的话,也就是 i i i组,分不出大于 i i i组的情况,在变遍历的过程中, j j j的取值应该是 m i n ( m , i ) min(m,i) min(m,i), m m m是题目中给出的分割数的上限
  • 最外层的 m i n min min如果初始值为0,会影响到结果,设置一个 M A X MAX MAX值,每次的

前缀和辅助数组

定义 p r e f i x [ i ] prefix[i] prefix[i]表示 n u m s nums nums数组中前 i i i个数的和,即 n u m s [ 0... i − 1 ] nums[0...i-1] nums[0...i1]之前的和,此前缀和数组初始化 n + 1 n+1 n+1, p r e f i x [ 0 ] prefix[0] prefix[0]冗余,最为辅助数组

image-20200727214902854.png

以图中的例子举例,打印的 d p dp dp如下

[[0,MAX,MAX],
[MAX,7,MAX],
[MAX,9,7],
[MAX,14,7],
[MAX,24,14],
[MAX,32,18]]

一个小函数

  • 填充二维数组,遍历行,对每个列进行填充
        for (int i = 0; i <= n; i++) {
            Arrays.fill(dp[i], Integer.MAX_VALUE);
        }

完整代码

    public int splitArray(int[] nums, int m) {
        int n = nums.length;
        int[][] dp = new int[n + 1][m + 1];
        for (int i = 0; i <= n; i++) {
            Arrays.fill(dp[i], Integer.MAX_VALUE);
        }
        int[] prefix = new int[n + 1];
        for (int i = 0; i < n; i++) {
            prefix[i + 1] = nums[i] + prefix[i];
        }
        dp[0][0] = 0;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= Math.min(m, i); j++) {
                for (int k = 0; k < i; k++) {
                    dp[i][j] = Math.min(dp[i][j], Math.max(dp[k][j - 1], prefix[i] - prefix[k]));
                }
            }
        }
//        System.out.println(JSON.toJSONString(dp));
        return dp[n][m];
    }
复杂度分析:
  • 时间复杂度: O ( M ∗ N ∗ N ) O(M*N*N) O(MNN) ,其中 N N N是数组 n u m s nums nums的长度, M M M是要分割的段数,三层 f o r for for l o o p loop loop k k k最大到 N N N
  • 空间复杂度: O ( M ∗ N ) O(M*N) O(MN) d p dp dp的空间
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值