题目
石子合并(一)
时间限制:
1000 ms | 内存限制:
65535 KB
难度:
3
-
描述
-
有N堆石子排成一排,每堆石子有一定的数量。现要将N堆石子并成为一堆。合并的过程只能每次将相邻的两堆石子堆成一堆,每次合并花费的代价为这两堆石子的和,经过N-1次合并后成为一堆。求出总的代价最小值。
-
输入
-
有多组测试数据,输入到文件结束。
每组测试数据第一行有一个整数n,表示有n堆石子。
接下来的一行有n(0< n <200)个数,分别表示这n堆石子的数目,用空格隔开
输出
- 输出总代价的最小值,占单独的一行 样例输入
-
3 1 2 3 7 13 7 8 16 21 4 18
样例输出
-
9 239
来源
- 经典问题 上传者
想法:这题我不会,学长给了我一个算法(超厉害):
它的步骤如下:
设序列是stone[],从左往右,找一个满足stone[k-1] <= stone[k+1]的k,找到后合并stone[k]和stone[k-1],再从当前位置开始向左找最大的j,使其满足stone[j] > stone[k]+stone[k-1],插到j的后面就行。一直重复,直到只剩下一堆石子就可以了。在这个过程中,可以假设stone[-1]和stone[n]是正无穷的。一开始我是按照他的思想来写代码的,可是遇到诸多问题,例如c语言中如何定义一个数为无穷大。在运算的过程中stone[-1]也给我带来了好多问题。所以我将stone[-1]改成了stone[0],stone[n]改成了stone[n+1],也没有将它们俩定义为无穷大,而是设为各堆石子的和。
代码:#include<stdio.h>
int main()
{
int stone[205],a,i,n,k,m,s=0,t=0,sum,max=0;
while(scanf("%d",&n) !=EOF){//直到文件结束
sum=0;//初始化
//将每堆石子的数量保存在数组中
for(i=1;i<n+1;i++)
{
scanf("%d",&stone[i]);
}
//将数组的开头和结尾设置为最大值
for(i=1;i<n+1;i++){//找出数组中的最大值
s=s+stone[i];
}
stone[0]=s;
stone[n+1]=s;
do{
//从左向右寻找stone[i-1]<=stone[i+1]
for(i=1;i<n+1;i++)
{
if(stone[i-1]<=stone[i+1])
{
k=i;//标记位置
t=stone[i]+stone[i-1];//合并
break;
}
}
sum=sum+t;//合并石子所需的价值
//去掉合并的两个数,形成新数组,即将后面的数向前推两位
for(i=k-1;i<n;i++){//此时数组有n个数
stone[i]=stone[i+2];
}
//从当前位置向左寻找第一个比t大的数,将t插到此数的后面
for(i=k-2;i>=0;i--)
{
if(stone[i]>t)
{
m=i;//标记大于t的数的位置
break; }
}
//形成新的数组
for(i=n;i>=m+2;i--){//空余出t的位置,即数组向后推一位,此时,数组应有n-1个数
stone[i]=stone[i-1];
}
stone[m+1]=t;//将t插入
n--;
}while(n>1);
printf("%d\n",sum);
}
return 0;
}
-
有多组测试数据,输入到文件结束。