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;
}

浙公网安备 33010602011771号