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\n",ans);
return 0;
}
上一篇:Frog Jump


下一篇:HDU - Hotel