洛谷 P5569 石子合并——题解

洛谷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$。


正片开始

套路做法:

  1. 先向后扫序列,找到第一个\(v_{k+1} \leq v_{k-1}\)\(k\),然后将\(v_{k-1}\)\(v_{k}\)合并,并从序列中删除,这个操作可以用\(vector\)进行维护。

  2. 然后从\(k-1\)的位置开始向前扫,找到第一个\(cnt\)使得\(v_{cnt} > v_{k} + v_{k-1}\) ,然后将$ v_{k} + v_{k-1}$ 的值塞到 \(cnt+1\) 的位置。

  3. 重复以上步骤直至序列中元素个数为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()-2;
    for(int i=0;i<v.size()-2;i++){if(v[i]<=v[i+2]){k=i;break;}}
    int tmp=v[k+1]+v[k];
    v.erase(v.begin()+k);
    v.erase(v.begin()+k);
    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\)

posted @ 2024-09-19 12:28  Nightmares_oi  阅读(14)  评论(0编辑  收藏  举报