3229: [Sdoi2008]石子合并
Description
在一个操场上摆放着一排N堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。
试设计一个算法,计算出将N堆石子合并成一堆的最小得分。
Input
第一行是一个数N。
以下N行每行一个数A,表示石子数目。
Output
共一个数,即N堆石子合并成一堆的最小得分。
Sample Input
4
1
1
1
1
Sample Output
8
HINT
对于 100% 的数据,1≤N≤40000
对于 100% 的数据,1≤A≤200
题解:
个人认为还是比较简单的(在知道了GarsiaWachs算法后)
我只知道结论,设一个序列是A[0..n-1],每次寻找最小的一个满足A[k-1]<=A[k+1]的k,(方便起见设A[-1]和A[n]等于正无穷大)
那么我们就把A[k]与A[k-1]合并,之后找最大的一个满足A[j]>A[k]+A[k-1]的j,把合并后的值A[k]+A[k-1]插入A[j]的后面。
有定理保证,如此操作后问题的答案不会改变。
举个例子:
186 64 35 32 103
因为35<103,所以最小的k是3,我们先把35和32删除,得到他们的和67,并向前寻找一个第一个超过67的数,把67插入到他后面
186 64(k=3,A[3]与A[2]都被删除了) 103
186 67(遇到了从右向左第一个比67大的数,我们把67插入到他后面) 64 103
186 67 64 103 (有定理保证这个序列的答案加上67就等于原序列的答案)
现在由5个数变为4个数了,继续!
186 (k=2,67和64被删除了)103
186 131(就插入在这里) 103
186 131 103
现在k=2(别忘了,设A[-1]和A[n]等于正无穷大)
234 186
420
最后的答案呢?就是各次合并的重量之和呗。420+234+131+67=852;
#include<stdio.h> #include<iostream> #include<algorithm> #include<string.h> using namespace std; const int Max=2000000000; int n,i,j,k,m,ans,a[40005]; int main() { scanf("%d",&n); for(i=1;i<=n;i++) scanf("%d",&a[i]); a[0]=Max; a[n+1]=Max; m=n; for(i=1;i<n;i++) { k=0; for(j=1;j<=m;j++) if(a[j-1]<=a[j+1]) { k=j; break; } a[k-1]+=a[k]; for(j=k;j<m;j++) a[j]=a[j+1]; k--; ans+=a[k]; while(k>0&&a[k-1]<=a[k]) { swap(a[k-1],a[k]); k--; } a[m]=Max; m--; } cout<<ans; return 0; }