GarsiaWachs算法:石子归并问题 (ccf 压缩编码 原型)

N堆石子摆成一条线。现要将石子有次序地合并成一堆。规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的代价。计算将N堆石子合并成一堆的最小代价。

 

例如: 1 2 3 4,有不少合并方法

1 2 3 4 => 3 3 4(3) => 6 4(9) => 10(19)

1 2 3 4 => 1 5 4(5) => 1 9(14) => 10(24)

1 2 3 4 => 1 2 7(7) => 3 7(10) => 10(20)

 

括号里面为总代价可以看出,第一种方法的代价最低,现在给出n堆石子的数量,计算最小合并代价。

Input

第1行:N(2 <= N <= 50000)
第2 - N + 1:N堆石子的数量(1 <= A[i] <= 10000)

Output

输出最小合并代价

Input示例

4
1
2
3
4

Output示例

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])
 	{
  		Combine(i-1);
 	}
}

 

 

ccf 压缩编码问题:

      http://blog.csdn.net/more_ugly_less_bug/article/details/60142954

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值