一、Problem
有 N 堆石頭排成一排,第 i 堆中有 stones[i] 塊石頭。
每次移動(move)需要將連續的 K 堆石頭合併爲一堆,而這個移動的成本爲這 K 堆石頭的總數。
找出把所有石頭合併成一堆的最低成本。如果不可能,返回 -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.
Note:
1 <= stones.length <= 30
2 <= K <= 30
1 <= stones[i] <= 100
二、Solution
方法一:記憶化搜索
- 定義狀態:
- 表示將區間 中的幾堆石子合併成 堆需要的最小代價
- 思考初始化:
- 石子不用動就可以了…
- 思考狀態轉移方程:
- 這裏直接寫轉移會比較繁瑣,所以直接講思路:因爲我們不知道合併哪些區間可以使得代價最小,所以我們需要窮舉一下每種大小的區間 len(len = r-l+1)
- 且在區間 [l, r] 中,我們也不清楚具體要合併哪兩對堆(如果將區間劃分成兩半)所以我們需要枚舉一下切分點 p 在區間 [l, r] 中的位置
- 切分位置 p 我們知道了,但是我們還不知道要將這兩部分石子合併成幾堆,所以我們還需要枚舉一下石子的對數
- 思考輸出:
class Solution {
public int mergeStones(int[] a, int K) {
int n = a.length, INF = 0x3f3f3f3f, s[] = new int[n+1], f[][][] = new int[n+1][n+1][n+1];
if ((n-1) % (K-1) != 0)
return -1;
if (n < 2)
return 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) {
Arrays.fill(f[i][j], INF);
}
for (int i = 1; i <= n; i++) {
s[i] = s[i-1] + a[i-1];
f[i][i][1] = 0;
}
for (int len = 2; len <= n; len++)
for (int l = 1; l <= n - len + 1; l++) {
int r = l + len - 1;
for (int k = 2; k <= len; k++)
for (int p = l; p < r; p++) {
f[l][r][k] = Math.min(f[l][r][k], f[l][p][k-1] + f[p+1][r][1]);
}
// 由於上面的k是遞增的,所以到達K時,f[l][r][K]一定是最小(最優)此時
f[l][r][1] = Math.min(f[l][r][1], f[l][r][K] + s[r] - s[l-1]);
}
return f[1][n][1];
}
}
複雜度分析
- 時間複雜度:,
- 空間複雜度:,