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 };