题目
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]
}