题意:和普通的石子合并区别在于n的范围,n≤50000。
解:GarsiaWachs算法。
算法流程不复杂:从左到右找出第一个满足ai-1<ai+1的位置,将ai-1和ai合并为temp,放到它们左边第一个大于等于temp的值的右边。这里的大于和小于都可以换成大于等于,小于等于。
下面口胡一下算法原理。真正的证明在这里:A New Proof of the Garsia-Wachs Algorithm
考虑只有三个石子的情况,令三个石子价值分别为a、b、c,那么答案等于min((a+b)+(a+b+c), (b+c)+(a+b+c)),也就是最终答案取决于a和c,如果a<c,那就先合并a和b;
考虑不限制只有相邻的两个石子才能合并。那就是构造一棵哈夫曼树,每次选两个最小的合并在一起。
现在把上面两点结合起来,可以理解为 n个有序键构造二叉查找树的问题(By OI Wiki)。首先对于序列中的一个三元组(ai-1, ai ,ai+1),满足ai-1<ai+1,将ai-1和ai合并,即放在树中同一高度,一定比将ai和ai+1放在一起更优。
然后证明将合并后的数放到前面是合法的。
首先证明换完之后答案不会更差。对于以ai开始往左第一个大于temp的值,设其为ak,原来排在它后面的为ak+1在树中深度为hk+1,hk+1要么等于hi - 1,要么等于hi。大概是前面这一段既没有满足ai-1<ai+1的三元组,不可能在ai-1之前被合并;也不大于ai-1+ai,不会在temp之后被合并。原文用了反证法,意为hk+1<hi-1时总有一种重构能使答案更优。在此基础上可以说明这样交换答案不会更差,因为它能换回去。
接着证明交换后依然可以通过重构这棵树获得同样的答案,来满足只能合并相邻两个数的条件。具体操作没看懂,大概还是进行一些位移。证到这里显然就证完了。代码用vector很好写,但要开O2优化。
代码:
#include <bits/stdc++.h> using namespace std; #define maxx 705 #define maxn 25 #define maxm 205 #define ll long long #define inf 1000000009 #define mod 1000000007 vector<int> v; int change(){ int len=v.size(); int k=v.size()-2; for(int i=0;i<=len-3;i++){ if(v[i]<=v[i+2]){ k=i; break; } } int temp=v[k]+v[k+1]; int j; for(j=k;j>=0;j--){ if(v[j]>=temp) break; } v.erase(v.begin()+k); v.erase(v.begin()+k); v.insert(v.begin()+j+1,temp); return temp; } //a b c //(a+b)+(a+b+c) //(b+c)+(a+b+c) signed main() { // int T; // scanf("%d",&T); // while(T--) {; // // } int n; scanf("%d",&n); for(int i=1;i<=n;i++){ int t; scanf("%d",&t); v.push_back(t); } int ans=0; for(int i=1;i<n;i++){ ans+=change(); } printf("%d",ans); return 0; }