把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

GarsiaWachs算法 && P5569 [SDOI2008] 石子合并 分析

题目概述

合并石子。

其中 \(n\leq 4\times 10^4\)

分析

贪心。

注意到我们如果要合并 \(a,b,c\) 为一个,那么:

  • 先合并前两个有:\(2a+2b+c\)
  • 县合并后两个有:\(a+2b+2c\)

我们发现跟 \(b\) 没有关系,只跟 \(a,c\) 有关系。

所以我们先找到一个 \(i\) 满足 \(a_{i-1}<a_{i+1}\),然后合并前面两个,删除他们两个。

然后再找到一个 \(k\) 满足 \(a_k>a_{i-1}+a_{i}\),将 \(a_{i-1}+a_i\) 放到 \(k\) 的后面。

前面的那个易于理解,后面的为什么是正确的呢?

首先将 \(a_{j+1\dots i-2}\) 看作一个整体,现在变成:\(a_j,a_{j+1\dots i-2},a_{i}+a_{i-1}\),由于 \(a_j>a_{i}+a{i-1}\) 所以说他们两个是不会先合并的,县合并的一定是 \(a_{i-1}+a_i\) 和中间的某个值,而前面的时候我们只考虑 \(a<c\) 的情况,所以挪到前面去是没有问题的。

代码

时间复杂度 \(\mathcal{O}(n^2)\),能过,可以用 \(set\) 优化到 \(\mathcal{O}(n\log n)\)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#include <algorithm>
#include <vector>
#define int long long
#define N 40004
using namespace std;
signed main(){
    int n;
    cin >> n;
    vector<int> ls(n + 2);
    ls[0] = 1e18;
    for (int i = 1;i <= n;i ++) scanf("%lld",&ls[i]);
    ls[n + 1] = 1e18;
    int ans = 0;
    while(n > 1) {
        int k = 0;
        for (k = 1;k <= n;k ++)
            if (ls[k - 1] < ls[k + 1]) break;
        int res = ls[k - 1] + ls[k],id = k;
        for (k --;k >= 0;k --)
            if (ls[k] > res) break;
        ls.erase(ls.begin() + id - 1);
        ls.erase(ls.begin() + id - 1);
        ls.insert(ls.begin() + k + 1,res);
        ans += res;
        n --;
        // for (auto i : ls) cout << i << ' ';
        // cout << '\n';
    }
    cout << ans;
    return 0;
}
posted @ 2025-10-21 09:17  high_skyy  阅读(2)  评论(0)    收藏  举报
浏览器标题切换
浏览器标题切换end