BZOJ-3229 石子合并 GarsiaWachs算法

经典DP?稳T

3229: [Sdoi2008]石子合并
Time Limit: 3 Sec Memory Limit: 128 MB
Submit: 426 Solved: 202
[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

Source

石子合并问题的专门算法GarsiaWachs算法:
先从序列中找第一个st【k】使得st【k-1】<=st【k+1】然后合并st【k-1】与st【k】;
再从序列中从k往前找第一个st【j】使得st【j】>st【k-1】+st【k】然后将这个合并后的放在j位置后;
如此往复直到只剩一堆;
此题暴力枚举即可,时间复杂度O(n^2)可以用平衡树去维护那个序列,实现O(nlogn)

对于证明,即是应用树结构的某最优balabala...

code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
    while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}

int st[50000];
int n,t;
int ans=0;

void work(int k)
{
    int tmp=st[k]+st[k-1];
    ans+=tmp;
    for (int i=k; i<t-1; i++)
        st[i]=st[i+1];
    t--;
    int j=0;
    for (j=k-1; j>0 && st[j-1]<tmp; j--)
        st[j]=st[j-1];
    st[j]=tmp;
    while (j>=2 && st[j]>=st[j-2])
        {
            int d=t-j;
            work(j-1);
            j=t-d;
        }
}

int main()
{
    n=read();
    for (int i=0; i<n; i++) st[i]=read();
    t=1;ans=0;
    for (int i=1; i<n; i++)
        {
            st[t++]=st[i];
            while (t>=3 && st[t-3]<=st[t-1])
                work(t-2);
        }
    while (t>1) work(t-1);
    printf("%d
",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/DaD3zZ-Beyonder/p/5346182.html