题目描述
有N堆石子围成一个圆,每堆石子有一定的数量。现要将N堆石子并成为一堆。合并的过程只能每次将相邻的两堆石子堆成一堆,每次合并花费的代价为这两堆石子的和,经过N-1次合并后成为一堆。求出总的代价最小值。
输入
有多组测试数据,输入到文件结束。
每组测试数据第一行有一个整数n,表示有n堆石子。
接下来的一行有n(0< n <200)个数,分别表示这n堆石子的数目,用空格隔开。
输出
输出总代价的最小值,占单独的一行。
样例输入
3
1 2 3
4
3 5 2 3
6
3 5 7 3 4 2
样例输出
9
26
60
题目解析:
在之前的直线形石子合并的优化中,我们利用了四边形不等式进行了优化,同样的,在环形石子合并中,我们依然可以采用同样的方法,如果对之前优化不太清楚的同学,可以查看我之前的博客,链接为https://blog.csdn.net/chenpeixing361/article/details/88742815。
我们依然采用上次的方法,划圆为直,把数组长度变为2*n-1,然后利用四边形不等式,得到最优决策下的k值,在之前的基础上,我们只需要加上一层循环判断区间[0,n-1],[1,n],......,[n-1,2*n-2]的代价最小值即可。
参考代码:
import java.util.Scanner;
/**
* 环形石子合并优化算法
* @author autumn_leaf
* @Date 2019/03/23
*/
public class CombinStones4 {
static int dpSum(int[] stone,int i,int j) {
int sum = 0;
for(int k=i; k<=j; k++) {
sum += stone[k];
}
return sum;
}
static int dpMethod(int[] stone) {
int n = stone.length;
int[][] dp = new int[n+1][n+1];
int[][] s = new int[n+1][n+1];
//数组初始化
for(int i=0; i<n; i++) {
dp[i][i] = 0;
s[i][i] = i;
}
//这边代码和之前直线形优化一样,不再叙述
for(int i=n-1; i>=0; i--) {
for(int j=i+1; j<n; j++) {
int temp = Integer.MAX_VALUE;
int fence = 0;
for(int k=s[i][j-1]; k<=s[i+1][j]; k++) {
int sum = dpSum(stone,i,j) + dp[i][k] + dp[k+1][j];
if(sum < temp) {
temp = sum;
fence = k;
}
}
//保存优化后的结果
dp[i][j] = temp;
s[i][j] = fence;
}
}
//相对于之前直线形优化,这边多一层循环依次判断最小值
//这边是n/2原因是实际传入的参数n为2*n
int min = Integer.MAX_VALUE;
for(int i=0; i<n/2; i++) {
if(min > dp[i][i+n/2-1]) {
min = dp[i][i+n/2-1];
}
}
return min;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while(sc.hasNext()) {
int n = sc.nextInt();
int[] stone = new int[2*n];
for(int i=0; i<n; i++) {
stone[i] = sc.nextInt();
stone[n+i] = stone[i];
}
System.out.println(dpMethod(stone));
}
}
}
运行截图如下:
好了,到这儿石子合并问题专题就结束了,有疑惑的同学可以在下方评论区留言哦!