最小代价树
时间限制(普通/Java) :
1000 MS/ 3000 MS 运行内存限制 : 65536 KByte
总提交 : 396 测试通过 : 100
总提交 : 396 测试通过 : 100
比赛描述
以下方法称为最小代价的字母树:给定一正整数序列,例如:4,1,2,3,在不改变数的位置的条件下把它们相加,并且用括号来标记每一次加法所得到的和。
例如:((4+1)+ (2+3))=((5)+(5))=10。除去原数不4,1,2,3之外,其余都为中间结果,如5,5,10,将中间结果相加,得到:5+5+10=20,那么数20称为此数列的一个代价,若得到另一种算法:(4+((1+2)+3))=(4+((3)+3))=(4+(6))=10,数列的另一个代价为:3+6+10=19。若给出N个数,可加N-1对括号,求出此数列的最小代价。
注:结果范围不超出longint.
输入
第一行为数N(1≤N≤200),第二行为N个正整数,整数之间用空格隔开。
输出
输出仅一行,即为最少代价值。
样例输入
4
4 1 2 3
样例输出
19
题目来源
OIBH 模拟赛
这是一个动态规划问题,可以参考《石子合并问题》。
并不是每次都取最小的两个数合并,就能得到全局最优解,所以贪心法是行不通的。
类似于单源最短路径的迪杰斯特拉( Dijkstra)算法,需要求出起点到所有点的局部最优解。
import java.util.Scanner;
public class Main{
private int n;// 个数
private int[] data;// 输入的数字
private int[][] cost;// cost[i][j]表示从i合并到j的最小代价
private int[] sum;// sum[j]表示从0到j的数字之和
public void input() throws Exception{
Scanner sc=new Scanner(System.in);
try {
// input
n=sc.nextInt();
if(n<1||n>200)
throw new Exception("n<1||n>200");
// 根据输入的n来创建数组
data=new int[n];
cost=new int[n][n];
sum=new int[n];
for(int i=0;i<n;i++){
int input=sc.nextInt();
if(input<0)
throw new Exception("input<0");
data[i]=input;
}
}catch(Exception e){
throw e;
}finally{
sc.close();
}
}
public void minCost(){
// 求从0到j的数字之和
sum[0]=data[0];
for(int j=1;j<n;j++){
sum[j]=sum[j-1]+data[j];
}
// 初始化cost矩阵,设为最大值
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
cost[i][j] = (i == j) ? 0 : Integer.MAX_VALUE;
}
}
// 创建最小代价矩阵
for(int v=1;v<n;v++){
for(int i=0;i<n-v;i++){
int j=i+v;
int sumIJ=sum[j] - ((i==0) ? 0 : sum[i-1]);
for(int k=i;k<j;k++){
cost[i][j]=Math.min(cost[i][j],cost[i][k]+cost[k+1][j]+sumIJ);
}
}
}
}
public static void main(String[] args) {
Main m=new Main();
try{
m.input();
m.minCost();
System.out.println(m.cost[0][m.n-1]);
} catch (Exception e) {
// e.printStackTrace();
}
}
}