有 N
堆石頭排成一排,第 i
堆中有 stones[i]
塊石頭。
每次移動(move)需要將連續的 K
堆石頭合並為一堆,而這個移動的成本為這 K
堆石頭的總數。
找出把所有石頭合並成一堆的最低成本。如果不可能,返回 -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,這是可能的最小值。
輸入:stones = [3,2,4,1], K = 3 輸出:-1 解釋:任何合並操作后,都會剩下 2 堆,我們無法再進行合並。所以這項任務是不可能完成的。.
輸入: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
當K=2時,每次合並都是相鄰的兩堆進行合並。用dp[i][j]表示從i到j這個區間合並為1個堆時的最小代價。那么有轉移方程:
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1])
//dp是從兩兩合並開始的,也就是說是長度先為2,然后3,....所以要枚舉len的長度 //dp[i][j],len=j-i for(int len=1;len<n;len++) { for(int i=1;i<=n-len;i++) { int j=i+len; for(int k=i;k<j;k++) { dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]); } } }
通過觀察上面的例子,我們可以知道K=2時,我們做的其實是將一部分合並為1堆,另一部分合並為1堆,最后形成一堆。典型的子問題划分問題,可以想象歸並排序的過程。
現在K!=2了,那么我們就將一部分合並成K-1堆,另一部分合並成1堆,然后合並。
至於為什么不是K-2堆和2堆以及K-3堆和3堆是因為我們的子問題是合並成1堆,當前狀態是由前一狀態得到的。而我們最初只有dp[i][i][1]=0這個條件
現在K可以為任意值,由樣例我們可以得到如果(n-1)%(k-1)不等於0的話,說明最終無法形成一堆。
現在考慮正常的情況,我們用dp[i][j][m]表示從i到j這個區間形成m堆所需的最小代價
初始化 dp[i][i][1]=0
dp[i][j][K]=min(dp[i][j][k],dp[i][k][K-1]+dp[k+1][j][1])
dp[i][j][1]=min(dp[i][j][K]+sum[j]-sum[k-1])
for (len=2;len<=n;++len){ for (l=1;l+len-1<=n;++l) { r=l+len-1; for (k=l;k<r;++k) { for (i=2;i<=len;++i) { f[l][r][i]=min(f[l][r][i],f[l][k][i-1]+f[k+1][r][1]); } } f[l][r][1]=min(f[l][r][K]+sum[r]-sum[l-1],f[l][r][1]); }