算法提高 合并石子

时间:2023-02-13 20:40:45

算法提高 合并石子
时间限制:2.0s 内存限制:256.0MB
提交此题
问题描述
  在一条直线上有n堆石子,每堆有一定的数量,每次可以将两堆相邻的石子合并,合并后放在两堆的中间位置,合并的费用为两堆石子的总数。求把所有石子合并成一堆的最小花费。
输入格式
  输入第一行包含一个整数n,表示石子的堆数。
  接下来一行,包含n个整数,按顺序给出每堆石子的大小 。
输出格式
  输出一个整数,表示合并的最小花费。
样例输入
5
1 2 3 4 5
样例输出
33
数据规模和约定
  1<=n<=1000, 每堆石子至少1颗,最多10000颗。

石子合并问题,采用GarsiaWachs算法:
找出序列中满足stone[i-1] <=stone[i+1]最小的i, 合并temp = stone[i]+stone[i-1], 接着往前面找是否
有满足stone[j] > temp, 把temp值插入stone[j]的后面(数组的右边). 循环个过程一直到只剩下一堆石子结束.

算法相当于用树形递归。

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
#define MAX 50005
int n;
int a[MAX];
int num, result;
void combine(int k)
{
int i, j;
int temp = a[k]+a[k-1];//合并前一位
result += temp;
for(i = k; i < num-1; ++i) //a[n+1]可以当成无穷大
a[i] = a[i+1];
num--;//合并,少一
for(j = k-1; j > 0 && a[j-1] < temp; --j)//找满足啊a[j-1]<temp的点,将temp插入a[j]的右边
a[j] = a[j-1];//a[-1]当成无穷小
a[j] = temp;
while(j >= 2 && a[j] >= a[j-2])
{
int d = num-j;
combine(j-1);


j = num-d;
}
}
int main()
{
int i;
while(scanf("%d", &n) != EOF)
{
if(n == 0) break;
for(i = 0; i < n; ++i)
scanf("%d", &a[i]);
num = 1;
result = 0;
for(i = 1; i < n; ++i)
{
a[num++] = a[i];
while(num >= 3 && a[num-3] <= a[num-1])
combine(num-2);
}
while(num > 1)
combine(num-1);
printf("%d\n", result);
}
return 0;
}