第1行:N(2 <= N <= 50000) 第2 - N + 1:N堆石子的数量(1 <= A[i] <= 10000)
输出最小合并代价
4 1 2 3 4
19
问题分析:
如果只有3堆石子,那么只有两种合并方法,它们的代价为s1、s2,那么有
s1 = (a+b)+((a+b)+c)
s2 = (b+c)+((b+c)+a)
假设s1<s2, 那么可以得出a<=c, 这说明只要a和c的关系确定,合并的顺序也确定
①:GarsiaWachs算法,就是基于上述的结论实现,找出序列中满足stone[i-1]<=stone[i+1]最小的i,
先合并temp = stone[i-1]+stone[i], 然后往前面找第一个满足stone[j]>temp的地方,(或找不到)
最后把temp值插入stone[j]的后面,循环这个过程直到只剩下一堆石子结束
②:为什么要将temp插入stone[j]的后面?可以这样理解:将stone[j+1]到stone[i-2]
看成一个整体stone[mid],这样状态就为stone[j],stone[mid],temp(stone[i-1]+stone[i]);
因为temp<stone[j],所以不管怎样都是stone[mid]和temp先合并,所以将temp值插入stone[j]的后面不会影响结果
解题过程:
①:从1到n遍历一遍数组,每当添加一个数时,如果当前最顶端的三个数a,b,c满足a<c,则执行操作③合并a和b,执行完毕后如果依旧如此继续执行操作③直到最顶端的三个数不满足条件a<c
②:遍历完毕后,如果剩下的数不止一个,执行操作③合并当前最顶端的两个直到只剩下一个数
③:合并a和b,令temp=a+b,先将a和b从数组中取出,然后将a右边的数全部往左移1位,将a左边所有小于temp的数全部往右移1位,最后在空位处补上temp,设temp所在位置为i,处理完毕后可能再次出现a[i-2]<a[i],如果出现继续执行步骤③将a[i-2]和a[i-1]合并,直到不满足当前a[i-2]<a[i]
#include<stdio.h>
#define LL long long
LL a[50005], n, k, ans;
void Combine(LL x);
int main(void)
{
LL i;
scanf("%lld", &n);
for(i=1;i<=n;i++)
scanf("%lld", &a[i]);
k = 1, ans = 0;
for(i=2;i<=n;i++)
{
a[++k] = a[i];
while(k>=3 && a[k-2]<=a[k]) /*①*/
Combine(k-2); /*合并a[k-2], a[k-1]*/
}
while(k>1) /*②*/
Combine(k-1);
printf("%lld\n", ans);
return 0;
}
void Combine(LL x) /*③*/
{
LL i, d, temp;
temp = a[x]+a[x+1];
for(i=x+1;i<=k-1;i++)
a[i] = a[i+1];
ans += temp, k -= 1;
for(i=x-1;i>=1&&a[i]<=temp;i--)
a[i+1] = a[i];
a[i+1] = temp;
while(i>=2 && a[i-1]<=a[i+1])
{
d = k-i;
Combine(i-1);
i = k-d;
}
}