题意
(n) 个石子排成一排,第 (i) 堆有 (a_i) 个,每次可以选取两个石子堆合并,得分是新石子堆的大小,求合并所有石子为一堆的最小得分。
(nleqslant 40000)。
分析
大众科技,GarsiaWachs 算法。
设当前石子共 (w) 堆,分别为 (v_{1cdots w})。
- 找到最小的 (k) 满足 (v_{k-1}<v_{k+1}),然后找到最大的 (p) 满足 (p<k) 且 (v_p>v_k+v_{k-1});
- 合并第 (k,k+1) 堆石子,并将其放在第 (p) 堆石子后面;
- 重复操作直到 (w=1)。
证明鸽了。
注意要在序列两端放置两个哨兵点。
复杂度:(O(n^2)),开 O2 能过,可以用平衡树优化到 (O(nlog n))。
实际上有一个扩展,但是我还不会。
代码
#include<stdio.h>
#include<vector>
#define inf 2000000000
using namespace std;
const int maxn=40005;
int n,ans;
int a[maxn];
vector<int>v;
int main(){
scanf("%d",&n);
v.push_back(inf);
for(int i=1,x;i<=n;i++)
scanf("%d",&x),v.push_back(x);
v.push_back(inf);
for(int i=1;i<n;i++){
int k=0,p=0,rec=0;
for(int j=1;j<=n;j++)
if(v[j-1]<v[j+1]){
k=j;
break;
}
rec=v[k]+v[k-1],ans+=rec;
for(int j=k-1;j>=0;j--)
if(v[j]>rec){
p=j;
break;
}
v.erase(v.begin()+k),v.erase(v.begin()+(k-1)),v.insert(v.begin()+p+1,rec);
}
printf("%d
",ans);
return 0;
}