题目描述

有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));
		}
	}
}

运行截图如下:

动态规划之石子合并四(环形优化)

好了,到这儿石子合并问题专题就结束了,有疑惑的同学可以在下方评论区留言哦!

相关文章: