Leetcode 1000. Minimum Cost to Merge Stones

231 篇文章 0 订阅

There are N piles of stones arranged in a row.  The i-th pile has stones[i] stones.

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))

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值