zoukankan      html  css  js  c++  java
  • 力扣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]
  • 相关阅读:
    android ListView美化-->几个比较特别的属性
    微博开发第一讲 知识点--2014.04.23
    ubuntu snap install 代理设置
    君正X2000开发板试用体验之二:君正X2000引导过程分析
    君正X2000开发板试用体验之一:开箱
    消除USB麦克风的电流声
    IT专业词汇中文译名探讨
    PCI/PCIE总线的历史
    龙芯派ejtag.cfg设置
    uos运行ejtag报错,找不到libreadline.so.6, libncurses.so.5, libusb-0.1.so.4
  • 原文地址:https://www.cnblogs.com/Pyrokine/p/12828432.html
Copyright © 2011-2022 走看看