https://www.lintcode.com/problem/segment-stones-merge/
有一个石子归并的游戏。最开始的时候,有n
堆石子排成一列,目标是要将所有的石子合并成一堆。合并规则如下:
- 每一次可以合并连续
x
堆石子,left <= x <= right
- 每次合并的代价为所合并的
x
堆石子的重量之和
求出最小的合并代价,如果无法完成合并返回0
。
1 <= n <= 100
,2 <= left <= right <= n
1 <= weight[i] <= 1000
您在真实的面试中是否遇到过这个题? 是
题目纠错
样例
Example 1:
Given n = `4`, left = `3`, right = `3`, weight = `[1,2,3,4]`, return `0`.
Input:
4
3
3
[1,2,3,4]
Output:
0
Explanation:
Unable to complete the merge.
Example 2:
Given n = `3`, left = `2`, right = `3`, weight = `[1,2,3]`, return `6`.
Input:
3
2
3
[1,2,3]
Output:
6
Explanation:
Merge 1,2,3, the merger cost is 1 + 2 + 3 = 6.
dp[i][j][k]表示在i,j范围内合并k堆石子最小的代价,dp[i][j][k]== -1时表示不能合并,dp[i][j][k]== -2时表示还未搜索到。注意当k==1时,拆分成left----min(j-i+1,right)种情况。复杂度O(n^4).此题是https://leetcode.com/problems/minimum-cost-to-merge-stones/的进化版。似乎这题递归的dp更容易实现。
public class Solution {
/**
* @param n: The number of stones
* @param left: The minimum length to merge stones
* @param right: The maximum length to merge stones
* @param weight: The weight array
* @return: Return the minimum cost
*/
int[][][] dp;
int[] sum;
int left,right,n;
public int getMinimumCost(int n, int left, int right, int[] weight) {
this.left=left;
this.right=right;
this.n=n;
dp=new int[n][n][right+1];
for(int[][] d1:dp){
for(int[] d2:d1)Arrays.fill(d2,-2);
}
sum=new int[n];
for(int i=0;i<n;i++)sum[i]=(i==0?0:sum[i-1])+weight[i];
dfs(0,n-1,1);
if(dp[0][n-1][1]<=-1)return 0;
return dp[0][n-1][1];
}
int dfs(int s,int e,int k){
if(dp[s][e][k]>=-1)return dp[s][e][k];
if(e-s+1<k){
dp[s][e][k]=-1;
}else if(s==e){
dp[s][e][k]=0;
}else{
dp[s][e][k]=99999999;
if(k==1){
int m=Math.min(right,e-s+1);
for(int i=left;i<=m;i++){
int v=dfs(s,e,i);
if(v!=-1){
dp[s][e][k]=Math.min(dp[s][e][k],v);
}
}
if(dp[s][e][k]!=99999999){
dp[s][e][k]+=sum[e]-(s==0?0:sum[s-1]);
}else{
dp[s][e][k]=-1;
}
}else{
for(int i=s+1;i<=e;i++){
int v1=dfs(s,i-1,k-1);
int v2=dfs(i,e,1);
if(v1!=-1&&v2!=-1){
dp[s][e][k]=Math.min(dp[s][e][k],v1+v2);
}
}
}
}
return dp[s][e][k];
}
}