leetcode1000. 合并石头的最低成本

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

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

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

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/minimum-cost-to-merge-stones

解决

经典的石子归并的题目,不过是三维的dp,想清楚是比较复杂的。记录一下。
dp状态和递推方程不难想到,但是怎么遍历想了很久,毕竟3维。子问题是解决小区间的合并,再大区间。


const INF = 1e8 + 7

var n int
var dp [][][]int

func Init(k int) {
	dp = make([][][]int, n)
	for i := 0; i < n; i++ {
		dp[i] = make([][]int, n)
		for j := 0; j < n; j++ {
			dp[i][j] = make([]int, k+1)
			for p := 0; p < k+1; p++ {
				dp[i][j][p] = INF
			}
		}
	}
}

func mergeStones(stones []int, k int) int {
	n = len(stones)
	Init(k)

	pre := make([]int, n+1)
	for i := 0; i < n; i++ {
		pre[i+1] = pre[i] + stones[i]
		dp[i][i][1] = 0
	}

	for ll := 1; ll <= n; ll++ { // 确定该轮遍历的区间长度
		for l := 0; l < n && l+ll < n; l++ { // 确定左,右边界
			r := l + ll
			for p := 2; p <= k; p++ { // 区间分p堆
				for s := l; s < r; s++ {
					dp[l][r][p] = min(dp[l][r][p], dp[l][s][1]+dp[s+1][r][p-1]) // dp[l][r][p] = min{ dp[l][s][1] + dp[s+1][r][p-1] }
					// fmt.Println("cp2", l, r, s, p, dp[l][r][p])
				}
			}
			dp[l][r][1] = min(dp[l][r][1], dp[l][r][k]+pre[r+1]-pre[l]) // dp[l][r][1] = dp[l][r][k] + sum[l,r]
		}
	}

	if dp[0][n-1][1] == INF {
		return -1
	}
	return dp[0][n-1][1]
}

func min(j, k int) int {
	if j < k {
		return j
	}
	return k
}

func main() {
	//stones := []int{1, 2, 3}
	//K := 2
	//fmt.Println(mergeStones(stones, K))

	stones := []int{3, 5, 1, 2, 6}
	K := 3
	fmt.Println(mergeStones(stones, K))

	stones = []int{1}
	K = 2
	fmt.Println(mergeStones(stones, K))

	stones = []int{5, 4}
	K = 3
	fmt.Println(mergeStones(stones, K))
}
posted @ 2023-04-06 17:38  happy_codes  阅读(43)  评论(0编辑  收藏  举报