There is a stone game.At the beginning of the game the player picks n
piles of stones in a line.
The goal is to merge the stones in one pile observing the following rules:
- At each step of the game,the player can merge two adjacent piles to a new pile.
- The score is the number of stones in the new pile.
You are to determine the minimum of the total score.
For [4, 1, 1, 4]
, in the best solution, the total score is 18
:
1. Merge second and third piles => [4, 2, 4], score +2
2. Merge the first two piles => [6, 4],score +6
3. Merge the last two piles => [10], score +10
Other two examples:
[1, 1, 1, 1]
return 8
[4, 4, 5, 9]
return 43
这是一道比较典型的区间类DP题目.如果从小到大合并,则这题很难选择.一个比较好的方式是自顶向下,先考虑最后一次合并的费用.每一次合并的附加费用是合并的两堆石块的总数目,即新石堆的石块数目.
我们可以枚举合并的位置,看哪个位置的成本最小,这个过程可以一直进行下去,直到一个石块的时候,合并成本是0.这中间因为枚举分割位,有非常多的重复区间.所以需要判断是否已经处理过. f的DP矩阵一开始初始化为-1,如果某个位置不是-1,则说明已经处理过,不必另外维护一个visited矩阵,另外,每次我们都要求区间和,也是一个重复性非常高的过程,可以提前求出.一个是直接求各个区间点的值,是二维矩阵,另外一个是先求前缀和数组,利用这个数组求区间和.显然后者空间复杂度低很多.代码如下:
class Solution: # @param {int[]} A an integer array # @return {int} an integer def stoneGame(self, A): # memory search if not A: return 0 f = [[-1]*len(A) for i in xrange(len(A))] for i in xrange(len(A)): f[i][i] = 0 cost = [0]*(len(A)+1) for i in xrange(1, len(A)+1): cost[i] = cost[i-1] + A[i-1] return self.search(A, f, cost, 0, len(A) - 1 ) def search(self, A, f, cost, start, end): if f[start][end] >= 0: return f[start][end] import sys f[start][end] = sys.maxint for k in xrange(start,end): left = self.search(A, f, cost, start, k) right = self.search(A, f, cost, k+1, end) f[start][end] = min(f[start][end], left + right + cost[end+1] - cost[start]) return f[start][end]
时间复杂度为O(n^3),枚举两边和切分位置.空间复杂度为O(n^2),DP矩阵。
这题有贪心做法,其实相当于霍夫曼编码,每次取最小的两堆做合并(包括新生成的堆)。这么做有可以用好几种思路,一种是排序,但是需要动态增加数字,堆是一种合理的做法。可以动态增删,另外也可以使用单调队列。具体见http://www.cnblogs.com/neverforget/archive/2011/10/13/ll.html 里面合并果子这一题。