leetcode 1000. 合并石头的最低成本(区间dp)

题意:

有 N 堆石头排成一排,第 i 堆中有 stones[i] 块石头。

每次移动(move)需要将连续的 K 堆石头合并为一堆,而这个移动的成本为这 K 堆石头的总数。

找出把所有石头合并成一堆的最低成本。如果不可能,返回 -1 。

 

示例 1:

输入:stones = [3,2,4,1], K = 2
输出:20
解释:
从 [3, 2, 4, 1] 开始。
合并 [3, 2],成本为 5,剩下 [5, 4, 1]。
合并 [4, 1],成本为 5,剩下 [5, 5]。
合并 [5, 5],成本为 10,剩下 [10]。
总成本 20,这是可能的最小值。

示例 2:

输入:stones = [3,2,4,1], K = 3
输出:-1
解释:任何合并操作后,都会剩下 2 堆,我们无法再进行合并。所以这项任务是不可能完成的。

示例 3:

输入:stones = [3,5,1,2,6], K = 3
输出:25
解释:
从 [3, 5, 1, 2, 6] 开始。
合并 [5, 1, 2],成本为 8,剩下 [3, 8, 6]。
合并 [3, 8, 6],成本为 17,剩下 [17]。
总成本 25,这是可能的最小值。

提示:

  • 1 <= stones.length <= 30
  • 2 <= K <= 30
  • 1 <= stones[i] <= 100

 

思路:

首先有解的条件为(n-1)%(K-1)==0

 

dp[i][j][k]表示把 第i堆石头到第j堆石头合并为k堆的最小代价(初始化dp[i][i][1]=0,显然,在执行过程中dp[i][j][j-i+1]都为0)

 

我们可以枚举中间点m,把左边分为k-1堆,把右边分为1堆

 

那么我们有: dp[i][j][k] = min(dp[i][j][k], dp[i][m][k-1] + dp[m+1][j][1])

 

为什么不能是左边k-2堆,右边2堆呢?考虑右边合并为两堆,那么我们可以找一个点,划到左边,就还是左边k-1堆,右边1堆啦,所以其他所有的情况实际上都已经被 左边k-1堆,右边1堆覆盖了。

 

还有一个方程就是dp[i][j][1] = dp[i][j][K] + sum[j] - sum[i-1],注意是大写的K,就是说如果有K堆了,那么我们可以把它们合并为一堆,代价为这些石头的总和。

 1 class Solution {
 2 public:
 3     int dp[50][50][50],pre[50];
 4     int mergeStones(vector<int>& stones, int K) {
 5         int n=stones.size();
 6         if((n-K)%(K-1))return -1;
 7         memset(dp,0x3f,sizeof(dp));
 8         for(int i=1;i<=n;i++){
 9             dp[i][i][1]=0;
10             pre[i]=pre[i-1]+stones[i-1];
11         }
12         for(int len=2;len<=n;len++){
13             for(int i=1;i+len-1<=n;i++){
14                 int j=i+len-1;
15                 for(int m=i;m<j;m++){
16                     for(int k=2;k<=len;k++){
17                         dp[i][j][k]=min(dp[i][j][k],dp[i][m][k-1]+dp[m+1][j][1]);
18                     }
19                 }
20                 dp[i][j][1]=min(dp[i][j][1],dp[i][j][K]+pre[j]-pre[i-1]); //如果dp[l][r][K]不存在,那么也就是说明不能把[l,r]这一段区间合成一段
21             }
22         }
23         return dp[1][n][1];
24     }
25 };
View Code
posted @ 2019-11-02 21:37  ljy3268  阅读(577)  评论(0编辑  收藏  举报