洛谷P5569题解
传送锚点
摸鱼环节
[SDOI2008] 石子合并
题目描述
在一个操场上摆放着一排 \(N\) 堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的 \(2\) 堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。
试设计一个算法,计算出将 \(N\) 堆石子合并成一堆的最小得分。
输入格式
第一行一个整数 \(N\)。
接下来 \(N\) 行,第 \(i\) 行一个整数 \(a_i\),代表第 \(i\) 堆石子的石子数。
输出格式
输出将所有石子合并为一堆的最小得分。
样例 #1
样例输入 #1
4
1
1
1
1
样例输出 #1
8
提示
$ N \leq 40000, a_i \leq 200$
请注意 \(N\) 的范围(来自上传者的提示)
神马双倍经验? 开什么玩笑,喜提TLE。瞅一眼数据范围,我嘞个 $ N \leq 40000, a_i \leq 200$。
正片开始
套路做法:
-
先向后扫序列,找到第一个\(v_{k+1} \leq v_{k-1}\)的\(k\),然后将\(v_{k-1}\)与\(v_{k}\)合并,并从序列中删除,这个操作可以用\(vector\)进行维护。
-
然后从\(k-1\)的位置开始向前扫,找到第一个\(cnt\)使得\(v_{cnt} > v_{k} + v_{k-1}\) ,然后将$ v_{k} + v_{k-1}$ 的值塞到 \(cnt+1\) 的位置。
-
重复以上步骤直至序列中元素个数为1。
至于算法的正确性,我有个绝妙的解释,但这里我写不下绝不是我不会。
完整代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+520,mod=9999973;
int n;
ll ans;
vector<int>v;
int findx()
{int k=v.size()-1;//初始化坐标for(int i=0;i<v.size()-2;i++){if(v[i]<=v[i+2]){k=i+1;break;}}int tmp=v[k-1]+v[k];v.erase(v.begin()+k-1);v.erase(v.begin()+k-1);int cnt=-1;//初始化坐标for(int i=k-1;i>=0;i--){if(v[i]>tmp){cnt=i;break;}}v.insert(v.begin()+cnt+1,tmp);return tmp;
}
int main()
{cin>>n;for(int i=1;i<=n;i++) {int x;cin>>x;v.push_back(x);}for(int i=0;i<n-1;i++) ans+=findx();cout<<ans<<endl;return 0;
}
完结收工!!!!!
个人主页
看完点赞,养成习惯
\(\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\)