leetcode | 1000. Minmum Cost to Merge Stones

题目

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

思路与解法

题目描述:现在我们有N堆石头,每堆石头有不同个数,我们需要将N堆石头合并为一堆,其中,在一次合并过程中只能合并相邻的K堆石头,所需代价为这K堆石头的个数和。N,K已知,如果存在一种可行方案,则给出所有可行合并方案中所需代价的最小值,否则输出-1。

首先,容易知道,如果存在一种可行的方案,则N、K必须满足 (N-1)%(K-1)=0。因为每次合并会减少K-1堆石头,最终剩余1堆石头,则整个合并过程一共减少了N-1堆石头,所以得到上式。因此,如果不满足上式,则返回-1即可。

之后,我们需要想一下如何才能计算整个合并过程的代价总和?
当我们将K堆石头合并为1堆之后,便需要对当前剩余的N-K+1堆石头进行合并……于是,此问题便是一个不断求解子问题从而求得最终解的问题,对于此类问题,我们可以使用分治、动态规划等方法来解决,然而此题目并不能明显的划分子问题,所以我们来到了动态规划。
由于我们合并的石头的为连续的K堆,所以我们便可以使用dp[i][j]来表示第i堆到第j堆合并后的最终代价(闭区间,合并结果不一定为1堆)。之后,我们便可以得到以下问题:i和j之间应该满足何种约束条件?
因为合并的区间长度为K,所以小于K的区间不用更新,置为0即可。当区间长度大于等于K时,我们可以得到如下状态转移方程:

# 如果区间长度大于K
dp[i][j]=min(dp[i][j], dp[i][k]+dp[k+1][j]) # 其中k的取值范围为k+=K-1,即k的更新步长为K-1
# 如果区间长度等于K
dp[i][j] = sum[j+1]-sum[i] # 其中,使用sum前缀和来提高运算效率,sum[i]表示前i堆石头的个数和

代码实现

这道题目我使用golang实现:

const INT_MAX = int(^uint(0) >> 1)
func min(a, b int) int {
    if a < b {
        return a
    }
    return b
}
func mergeStones(stones []int, K int) int {
    sum := make([]int, 1)
    if (len(stones)-1)%(K-1) != 0 {
        return -1
    }
    dp := make([][]int, 0)
    for i:=1; i<=len(stones); i++ {
        sum = append(sum, sum[i-1] + stones[i-1])
        dp = append(dp, make([]int, len(stones)))
    }

    for length:=K; length<=len(stones); length++ {
        for i:=0; i+length<=len(stones); i++ {
            j := i+length-1
            dp[i][j]=INT_MAX
            for k:=i ;k<j; k+=K-1 {
                dp[i][j] = min(dp[i][j], dp[i][k]+dp[k+1][j])
            }
            if (j-i)%(K-1) == 0 {
                dp[i][j] += sum[j+1]-sum[i]
            }
        }
    }
    return dp[0][len(stones)-1]
}

运行结果

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/liuyh73/article/details/88642905