码迷,mamicode.com
首页 > 其他好文 > 详细

【BZOJ 3229】 [Sdoi2008]石子合并

时间:2015-04-24 14:28:21      阅读:290      评论:0      收藏:0      [点我收藏+]

标签:bzoj   oi   garsiawach   

3229: [Sdoi2008]石子合并

Time Limit: 3 Sec Memory Limit: 128 MB
Submit: 312 Solved: 148
[Submit][Status][Discuss]
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算法。

直接dp可以用四边形不等式优化到O(n2),用GarsiaWachs是O(nlogn)的。

简述一下GarsiaWachs算法的流程:
【假设a[0]=a[n+1]=inf
1.从序列的左端开始找第一个a[k?1]a[k+1]k,然后合并a[k?1],a[k]

2.从当前位置开始向左找第一个a[j]>a[k?1]+a[k]j,把合并后的值插到j的后面

3.一直这样重复下去直到剩下一堆

具体算法证明:《The Art of Computer Programming》第3卷6.2.2节Algorithm G和Lemma W,Lemma X,Lemma Y,Lemma Z。
(还没有看。。)

#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#define LL long long
using namespace std;
int t,a[50005],n;
LL ans=0;
void Combine(int k)
{
    int tmp=a[k-1]+a[k];
    ans+=tmp;
    for (int i=k;i<t-1;i++)
        a[i]=a[i+1];
    t--;
    int j=0;
    for (j=k-1;j>0&&a[j-1]<tmp;j--)
        a[j]=a[j-1];
    a[j]=tmp;
    while (j>=2&&a[j]>=a[j-2])
    {
        int d=t-j;
        Combine(j-1);
        j=t-d;
    }
}
int main()
{
    scanf("%d",&n);
    ans=0;
    for (int i=0;i<n;i++)
        scanf("%d",&a[i]);
    t=1;
    for (int i=1;i<n;i++)
    {
        a[t++]=a[i];
        while (t>=3&&a[t-3]<=a[t-1])
            Combine(t-2);
    }
    while (t>1)
        Combine(t-1);
    cout<<ans<<endl;
    return 0;
}

技术分享

感觉这道题代码实现很巧妙,证明是通过转化到树上blablabla..

【BZOJ 3229】 [Sdoi2008]石子合并

标签:bzoj   oi   garsiawach   

原文地址:http://blog.csdn.net/regina8023/article/details/45244733

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!