There are N
piles of stones arranged in a row. The i
-th pile has stones[i]
stones.
A move consists of merging exactly K
consecutive piles into one pile, and the cost of this move is equal to the total number of stones in these K
piles.
Find the minimum cost to merge all piles of stones into one pile. If it is impossible, return -1
.
Example 1:
Input: stones = [3,2,4,1], K = 2 Output: 20 Explanation: We start with [3, 2, 4, 1]. We merge [3, 2] for a cost of 5, and we are left with [5, 4, 1]. We merge [4, 1] for a cost of 5, and we are left with [5, 5]. We merge [5, 5] for a cost of 10, and we are left with [10]. The total cost was 20, and this is the minimum possible.
Example 2:
Input: stones = [3,2,4,1], K = 3 Output: -1 Explanation: After any merge operation, there are 2 piles left, and we can't merge anymore. So the task is impossible.
Example 3:
Input: stones = [3,5,1,2,6], K = 3 Output: 25 Explanation: We start with [3, 5, 1, 2, 6]. We merge [5, 1, 2] for a cost of 8, and we are left with [3, 8, 6]. We merge [3, 8, 6] for a cost of 17, and we are left with [17]. The total cost was 25, and this is the minimum possible.
Note:
1 <= stones.length <= 30
2 <= K <= 30
1 <= stones[i] <= 100
----------------------------------------------
这题眨眼一看和https://blog.csdn.net/taoqick/article/details/104558765 很像,所以直接DFS,像代码mergeStones1中所示。涉及到一个问题,如果[start,i)剩下石头个数为a,[i,end)剩下石头个数为b,在a+b>K的情况下,整体还能再merge一次,这也是DP的难点,由于想不好怎么解决,所以只能把每个阶段merge到不能merge的石头状态记录下来,用DFS,居然过了。。。
后来花了很长时间想怎么自底向上的DP。还是用dp[i][j]表示从下标i到下标j merge到不能再merge状态的cost,那么怎么找子问题,刚才那个问题又来了。分析一下dp[i][j]的最终摆放状态:如果(j-i)%(K-1)==0,说明整体能merge成一块,这时候dp[i][j]在子问题的基础上再加上整段的和;在(j-i)%(K-1)!=0时,既然不能再merge了,说明一定有从未merge进某一堆的初始石头,而且个数是在(j-i)%(K-1)!=0。再分析一下从未merge进某一堆的初始石头的摆放状态:
- 如果摆放在最左边,那么dp[i+1][j]根据DP推导已经是merge到不能merge再merge情况下最有的解。那么dp[i][j]可能是dp[i][i]+dp[i+1][j]
- 如果摆放在最右边,那么dp[i][j-1]根据DP推导已经是merge到不能merge再merge情况下最有的解。那么dp[i][j]可能是dp[i][j-1]+dp[j][j]
- 如果碎石摆放在中间,说明左边一定merge过,而且右边又最优,那么dp[i][j]可能是dp[i][i+x(K-1)]+dp[i+x(K-1)][j],x需要从左到右滑动
上述这三种情况的讨论非常巧妙,枚举出了所有可能子问题的情况,而dp[i][j]恰好是在这些子问题中挑出最小的,那么就可以有mergeStones的代码。最后附上两种解法的代码:
import sys
class Solution:
def dfs(self, stones, start, end, K, mem):
l = end-start
if (l < K):
return 0, stones[start:end] #cost, merged stones
key = '{0}_{1}'.format(start,end)
if (key in mem):
return mem[key]
if (l == K):
total = sum(stones[start:end])
mem[key] = total, [total]
return total, [total]
cost,merged = sys.maxsize,stones[start:end] #bug2: stones instead of stones[start:end]
for i in range(start+1, end):
#print('start={0} i={1} end={2}'.format(start,i,end))
pre_v, pre_s = self.dfs(stones, start, i, K, mem)
suf_v, suf_s = self.dfs(stones, i, end, K, mem)
pre_l,suf_l = len(pre_s),len(suf_s)
#print('pre_l={0} suf_l={1}'.format(pre_l,suf_l))
if (pre_l+suf_l < K and pre_v+suf_v < cost):
cost,merged = pre_v+suf_v,pre_s+suf_s
elif (pre_l+suf_l >= K): #written as pre_l+suf_l >= l
tmp = pre_s + suf_s
for j in range(0, pre_l+suf_l-K+1):
tmpsum = sum(tmp[j:j+K])
tmpcost = tmpsum+pre_v+suf_v
if (tmpcost < cost):
cost,merged=tmpcost,tmp[:j]+[tmpsum]+tmp[j+K:]
mem[key] = cost,merged
#print(mem)
return mem[key]
def mergeStones1(self, stones, K):
l = len(stones)
if ((l-1)%(K-1) > 0):
return -1
res = self.dfs(stones, 0, l, K, {})
return res[0]
def mergeStones(self, stones, K):
l = len(stones)
if ((l-1)%(K-1) > 0):
return -1
dp = [[0 for j in range(l)] for i in range(l)]
for width in range(K, l+1):
for i in range(0, l+1-width):
j = i+width-1
dp[i][j] = sys.maxsize
for mid in range(i,j,K-1):
dp[i][j] = min(dp[i][j], dp[i][mid]+dp[mid+1][j])
if ((j-i)%(K-1) == 0):
dp[i][j] += sum(stones[i:j+1])
print(dp)
return dp[0][l-1]
s = Solution()
print(s.mergeStones([3,5,1,2,6],3))