直线上n堆石头,个数不等,每次可以合并相邻的两堆石头,并把合并后的石头个数作为此次得分,问最大得分是多少?
方法1:
递归,自顶向下,注意定义二维数组,空间换时间。
public class Stone {
//方法1 递归
private int[][] value;
private int[][] sum;
public Stone(int[] a){
int n = a.length;
value = new int[n][n];
sum = new int[n][n];
for(int i=0; i<n; i++){
sum[i][i] = a[i];
for(int j=i+1; j<n; j++){
sum[i][j] = sum[i][j-1] + a[j];
}
}
}
public int merge(int[] a, int start, int end){
if(value[start][end] > 0){
return value[start][end];
}
if(end == start){
return 0;
}
int max = 0;
for(int i=start; i<end; i++){
int score1 = merge(a, start, i);
int score2 = merge(a, i+1, end);
int result = score1 + score2 + sum[start][end];
if(result > max){
max = result;
}
}
value[start][end] = max;
return max;
}
}
方法2:
动态规划。自底向上。
设f为目标函数,
f(i, j)=max{f(i,k) + f(k+1, j)} + sum(i, j)
( i <= k < j )
v依赖于红色的x点,对角线上的x点表示只有一堆石头,可以初始化为0.以斜线为单位,向右上方推进,最后一点p为所求。
public int score(int[] a, int n){
int[][] v = new int[n][n];
for(int len=1; len<n; len++){//一共循环多少次(即多少条斜线)
for(int i=0; i<n-len; i++){//每一条斜线上要处理多少个点
int j = i + len;
for(int k=i; k<j; k++){//处理每个点时,交叉线有多少条
int value = v[i][k] + v[k+1][j] + sum[i][j];
if(v[i][j] < value){
v[i][j] = value;
}
}
}
}
return v[0][n-1];
}
可以用四边形不等式继续优化第三层循环。