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