• 力扣leetcode1000.合并石头的最低成本


    本文算是对其他人答案的解释吧

    根据石头数量(即数组长度N)生成NxN的矩阵,每个位置 [i, j] 表示的含义为 i 到 j 的所有合成方式中的最小值

      假设数据如上图所示,合成为三个一合并,那么,只有下图中深蓝色区域为有效区域,其他位置赋0。以第一行为例,0-0和0-1为无效合并,0-2为第一个有效的合并,直到0-6都是有效合并,第四行唯一有效的为4-6,这也是长度为3时最后一个有效的合并

     那么,需要确定计算顺序,最后一次计算是 0-6 全部合并,即填入右上角那个格子,合并三个石头,这三个中其中两个可以是由其他石头合并来的,如下图左,也可以其中一个是由其他合并来的,另外两个来自最初的石头,如下图右,当然,如果石头更多,则可以全都来自于合并后的石头

     但如果是K个合并,则问题会变得很复杂,因此将合并简化为左侧石头和右侧石头合并,并保证每侧都是有效的,如果将左侧定义为1个石头(原始的1个或者合并成后的一个),右侧为 K-1 个石头,这样左侧就有固定解了,而右侧长度小于原长度,即变为子问题,即

     step = K - 1, left = 1 + n * step  ①

     两侧的数量:[left, N - left]   ②   n取所有可能的值,例如在合并长度为7时,所有子问题为 [1, 6]、[3, 4]、[5, 2]  ③

     注:式①变形可得(left - 1) / (K - 1) = n,意为该长度有效,这也是最开始用来检测命题是否有效的公式

     这样就将问题拆解为重复子问题了,子问题为每个区间 [i, j] 的最小合并值,然后对②的所有子问题求最小值,即两侧分别为 1 个和 6 个、 3 个和 4 个、 5 个和 2 个时,哪种合并能得到最小值,然后子问题递归求解,而递归的逆运算即为递推(动态规划)了,dp 不会求解重复子问题,自然速度也快一些

     再对③推一层,假设 [3, 4] 是最小的解,这里面 3 是能够通过同样方式计算出来的,4 就有点不同,拆开是 [1, 3] 和 [3, 1] ,可以理解为一个存在解的石头 3 和一个多余的石头 1 ,计算时就用存在解的 3 的合并值额外加那 1 个石头的值即可,不存在额外的加分

     而额外加分指的是题目中的合并后需要将当前合累加进合并值,即 i 到 j 所有值累加后加到合并值上,观察最小子问题可发现,可以换一个角度理解什么时候需要额外加分,即假设 K = 5 ,已经存在一组合并好的石头,然后额外多 1 个、 2 个......这并不会额外加分,只用累加每个的值即可,如果这个是命题,则直接判否,直到额外多了 4 个,可以和最开始合并好的石头组成 5 个时,即可额外加分

    因此,在后续每层计算时,采用相同的思路,如果没有达到倍数时,就单纯找到最小值,然后累加,当达到倍数时,额外加分,其中某些子问题不是真命题,但是有实际意义

    附上代码 

    https://www.jiuzhang.com/solution/minimum-cost-to-merge-stones/#tag-other-lang-python
     1 class Solution:
     2     def mergeStones(self, stones, K):
     3         n = len(stones)
     4 
     5         if (n - 1) % (K - 1) != 0:
     6             return -1
     7 
     8         f = [[0 for _ in range(n)] for _ in range(n)]
     9 
    10         prefix = [0 for _ in range(n + 1)]
    11         for i in range(n):
    12             prefix[i + 1] = prefix[i] + stones[i]
    13 
    14         for l in range(K - 1, n):
    15             for i in range(n - l):
    16                 j = i + l
    17                 f[i][j] = float('inf')
    18                 for m in range(i, j, K - 1):
    19                     f[i][j] = min(f[i][j], f[i][m] + f[m + 1][j])
    20                 if (j - i) % (K - 1) == 0:
    21                     f[i][j] += prefix[j + 1] - prefix[i]
    22 
    23         return f[0][n - 1]
  • 相关阅读:
    MyBatis学习教程
    【转载】Spring MVC 整合 Freemarker
    Java高效编程之四【C语言结构的替代】
    String相关的问题
    接口与抽象类的区别
    Java Garbage Collection基础详解------Java 垃圾回收机制技术详解
    数据库事物、隔离等级及数据库锁机制
    hadoop 多表join:Map side join及Reduce side join范例
    Java IO设计模式彻底分析 (转载)
    傅里叶变换的智慧[转]
  • 原文地址:https://www.cnblogs.com/Pyrokine/p/12828432.html
Copyright © 2020-2023  润新知