bzoj 3229: [Sdoi2008]石子合并

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;
}

 

 

posted @ 2016-04-23 20:25  lwq12138  阅读(1093)  评论(0编辑  收藏  举报