有 n 堆石頭排成一排,第 i 堆中有 stones[i] 塊石頭。
每次 移動 需要將 連續的 k 堆石頭合併為一堆,而這次移動的成本為這 k 堆中石頭的總數。
返回把所有石頭合併成一堆的最低成本。如果無法合併成一堆,返回 -1 。
1. 字首和 + 動態規劃
首先分析需要合併(n-k)/(k-1)+1 次
或者說石頭個數需要滿足(n-k)%(k-1)==0
由於頻繁用到一定區間石頭數量和,首先使用字首和預先計算
class Solution {
public:
int mergeStones(vector<int>& stones, int k) {
int n = stones.size();
if(n==1) return 0;
if(n<k) return -1;
if((n-k)%(k-1)!=0) return -1;
//需要合併(n-k)/(k-1)+1 次
//定義dp[i][j][p]把i,j的石頭合併成p堆的最小成本
vector<int> presum(n+1);
for(int i=0;i<n;i++)
presum[i+1] = presum[i] + stones[i];
int memo[n][n][k+1];
memset(memo,-1,sizeof(memo));
function<int(int,int,int)> dfs = [&](int i,int j,int p)->int{
if(memo[i][j][p]!=-1) return memo[i][j][p];//剪枝
if(p==1){//合成一堆
if(i==j) return memo[i][j][p]=0;//邊界條件1,本來一堆無需合成,成本為0
return memo[i][j][p] = dfs(i,j,k) + presum[j+1]-presum[i];
}
memo[i][j][p] = INT_MAX;
for(int m=i;m<j;m+=k-1)
memo[i][j][p] = min(memo[i][j][p], dfs(i,m,1)+dfs(m+1,j,p-1));
return memo[i][j][p];
};
return dfs(0,n-1,1);
}
};