题目:
描述
以下方法称为最小代价的字母树:给定一正整数序列,例如: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
分析:
该题类似石子合并问题:
在一条直线上摆着N堆石子,现要将石子有序的合并成一堆,规定如下:每次只能移动相邻的2堆石子合并,合并花费为将的一堆石子的数量。设计一个算法,将这N堆石子合并成一堆的总花费最小(或最大)。
如果熟悉矩阵连乘对这类问题肯定非常了解。矩阵连乘每次也是合并相邻两个矩阵(只是计算方式不同)。那么石子合并问题可用矩阵连乘的方法来解决。
那么最优子结构是什么呢?如果有N堆,第一次操作肯定是从n-1个对中选取一对进行合并,第二次从n-2对中选取一对进行合并,以此类推……
设f[i][j]表示i-j合并的最优值, sum[i][j]表示第i堆石子到第j堆石子的总数量,递推公式如下:
(矩阵连乘可见《算法导论》,讲的很详细)
下面给出代码:
#include <stdio.h>
#include <limits.h>
int N,arr[210],sum[210][210],m[210][210];
void calSum();
void calMinSum();
int main()
{
int i;
scanf("%d",&N); //输入元素个数
for (i=0;i<N;i++)
{
scanf("%d",&arr[i]); //为数组赋值
}
calMinSum(); //计算最小代价和
return 0;
}
void calSum()
{
int i,j,k;
for (i=0;i<N;i++) //计算从i到j的元素和
{
for (j=i;j<N;j++)
{
for (k=i;k<=j;k++)
{
sum[i][j]+=arr[k];
}
}
}
}
void calMinSum()
{
int r,i,j,k,iTemp; //r为链长
calSum();
for (i=0;i<N;i++)
{
m[i][i]=0;
}
for (r=2;r<=N;r++)
{
for (i=0;i<N-r+1;i++)
{
j=i+r-1;
m[i][j]=INT_MAX;
for (k=i;k<=j-1;k++)
{
iTemp=m[i][k]+m[k+1][j]+sum[i][j];
if (iTemp<m[i][j])
{
m[i][j]=iTemp;
}
}
}
}
printf("%d\n",m[0][N-1]);
}