「学习笔记」Garsia-Wachs 算法
前言
本文的资料和图片均来自 \(\texttt{OI-Wiki}\)。
引入
题目描述
在一个操场上摆放着一排 \(N\) 堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的 \(2\) 堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。
试设计一个算法,计算出将 \(N\) 堆石子合并成一堆的最小得分。
\((N \leq 40000)\)
过程
我们看到这个题,自然而然会想到区间 DP,即朴素的做法。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll r[600], g[600];
ll dp[600][600];
int main() {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; ++ i) {
scanf("%lld", &r[i]);
r[i + n] = r[i];
g[i] = g[i - 1] + r[i];
dp[i][i] = 0;
}
for (int i = n + 1; i <= 2 * n; ++ i) {
dp[i][i] = 0;
g[i] = g[i - 1] + r[i];
}
for (int l = 1; l < n; ++ l) {
for (int i = 1, j = i + l; i < n * 2 && j <= n * 2; ++ i, j = i + l) {
dp[i][j] = 100000000;
for (int k = i; k < j; ++ k) {
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j] + g[j] - g[i - 1]);
}
}
}
ll minn = 0x3f3f3f3f;
for (int i = 1; i <= n; ++ i) {
minn = min(minn, dp[i][i + n - 1]);
}
printf("%lld", minn);
return 0;
}
交上去后,你会发现,RE 了 \(7\) 个。
为什么?
因为 \(n\) 太大了,二维数组开不下,其次就算是用了什么不为人知的手段开下了这么大的数组,\(n^2\) 的复杂度也铁定超时。
这可怎么办呢?
下面介绍一种专门处理石子合并这类问题的算法——Garsia-Wachs 算法
Garsia-Wachs 算法
Garsia-Wachs 的步骤如下:
在序列的两端设置极大值。
在序列中找到前三个连续的权重值 \(x, y, z\) 使得 \(x \leq z\)。因为序列结尾的最大值大于之前的任意两个有限值,所以总是存在这样的三元组。
从序列中移除 \(x\) 和 \(y\),并在原来 \(x\) 的位置以前大于或等于 \(x+y\) 且距 \(x\) 最近的值的右边重新插入元素,元素值为 \(x+y\)。因为左端最大值的存在,所以总是存在这样的位置。
为了有效地实现这一阶段,该算法可以在任何平衡二叉查找树结构中维护当前值序列。这样的结构允许我们在对数时间内移除 \(x\) 和 \(y\),并重新插入新节点 \(x + y\)。
在每一步中,数组中位于偶数索引上直到 \(y\) 值的权重形成了一个递减序列,位于奇数索引位的权重形成另一个递减序列。因此,重新插入 \(x+y\) 的位置可以通过在对数时间内对这两个递减序列使用平衡树执行两次二分查找找到。通过从前一个三元组 \(z\) 值开始的线性顺序搜索,我们可以在总线性时间复杂度内执行对满足 \(x \leq z\) 的第一个位置的搜索。
如果实在不会平衡树,vector 的 insert 和 erase 操作也是个不错的选择呢!
Garsia-Wachs 算法的总时间复杂度为 \(O(n\log n)\),时间复杂度证明?我只能说,学 OI 记住结论就好了,证明,那是数学要考虑的事,不是 OI 要考虑的事 考试又不会让你证明时间复杂度
至于正确性的证明我也不会= =,这个算法应用范围十分有限,因此学的价值不是很高,“会用” + “知道有这个东西” 就行了
关于上面那道引入题的代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll read() {
ll x = 0;
int fg = 0;
char ch = getchar();
while (ch < '0' || ch > '9') {
fg |= (ch == '-');
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return fg ? ~x + 1 : x;
}
const int N = 4e4 + 5;
int n, ans;
vector<int> g;
int merge() {
int k = g.size() - 2;
for (int i = 0; i <= k; ++ i) {
if (g[i] <= g[i + 2]) {
k = i;
break;
}
}
int tmp = g[k] + g[k + 1];
g.erase(g.begin() + k);
g.erase(g.begin() + k);
int t = -1;
for (int i = k - 1; i >= 0; -- i) {
if (g[i] >= tmp) {
t = i;
break;
}
}
g.insert(g.begin() + t + 1, tmp);
return tmp;
}
int main() {
n = read();
for (int i = 1; i <= n; ++ i) {
g.emplace_back(read());
}
for (int i = 1; i < n; ++ i) {
ans += merge();
}
printf("%d\n", ans);
return 0;
}
「学习笔记」Garsia-Wachs 算法的更多相关文章
- 「学习笔记」Min25筛
「学习笔记」Min25筛 前言 周指导今天模拟赛五分钟秒第一题,十分钟说第二题是 \(\text{Min25}\) 筛板子题,要不是第三题出题人数据范围给错了,周指导十五分钟就 \(\text{AK ...
- 「学习笔记」字符串基础:Hash,KMP与Trie
「学习笔记」字符串基础:Hash,KMP与Trie 点击查看目录 目录 「学习笔记」字符串基础:Hash,KMP与Trie Hash 算法 代码 KMP 算法 前置知识:\(\text{Border} ...
- 「学习笔记」FFT 之优化——NTT
目录 「学习笔记」FFT 之优化--NTT 前言 引入 快速数论变换--NTT 一些引申问题及解决方法 三模数 NTT 拆系数 FFT (MTT) 「学习笔记」FFT 之优化--NTT 前言 \(NT ...
- 「学习笔记」FFT 快速傅里叶变换
目录 「学习笔记」FFT 快速傅里叶变换 啥是 FFT 呀?它可以干什么? 必备芝士 点值表示 复数 傅立叶正变换 傅里叶逆变换 FFT 的代码实现 还会有的 NTT 和三模数 NTT... 「学习笔 ...
- 「学习笔记」Treap
「学习笔记」Treap 前言 什么是 Treap ? 二叉搜索树 (Binary Search Tree/Binary Sort Tree/BST) 基础定义 查找元素 插入元素 删除元素 查找后继 ...
- 「学习笔记」平衡树基础:Splay 和 Treap
「学习笔记」平衡树基础:Splay 和 Treap 点击查看目录 目录 「学习笔记」平衡树基础:Splay 和 Treap 知识点 平衡树概述 Splay 旋转操作 Splay 操作 插入 \(x\) ...
- 「学习笔记」wqs二分/dp凸优化
[学习笔记]wqs二分/DP凸优化 从一个经典问题谈起: 有一个长度为 \(n\) 的序列 \(a\),要求找出恰好 \(k\) 个不相交的连续子序列,使得这 \(k\) 个序列的和最大 \(1 \l ...
- 「学习笔记」ST表
问题引入 先让我们看一个简单的问题,有N个元素,Q次操作,每次操作需要求出一段区间内的最大/小值. 这就是著名的RMQ问题. RMQ问题的解法有很多,如线段树.单调队列(某些情况下).ST表等.这里主 ...
- 「学习笔记」递推 & 递归
引入 假设我们想计算 \(f(x) = x!\).除了简单的 for 循环,我们也可以使用递归. 递归是什么意思呢?我们可以把 \(f(x)\) 用 \(f(x - 1)\) 表示,即 \(f(x) ...
- 「学习笔记」min_25筛
前置姿势 魔力筛 其实不看也没关系 用途和限制 在\(\mathrm{O}(\frac{n^{0.75}}{\log n})\)的时间内求出一个积性函数的前缀和. 所求的函数\(\mathbf f(x ...
随机推荐
- python3各数据类型的常用方法
python3数据类型包括: 数字.字符串str.列表list.元组tuple.字典dict.集合set.布尔bool 1.字符串(str)-可变-用"".''定义 (1)uppe ...
- ASP.NET Core - 选项系统之选项验证
就像 Web Api 接口可以对入参进行验证,避免用户传入非法的或者不符合我们预期的参数一样,选项也可以对配置源的内容进行验证,避免配置中的值与选项类中的属性不对应或者不满足预期,毕竟大部分配置都 ...
- 在已有的vue项目中添加单元测试模块
package.json 添加新命令 "test": "jest",下载包: "jest": "^26.6.3", &q ...
- gulp基本操作
1.安装淘宝镜像 npm install cnpm -g --registry=https://registry.npm.taobao.org cnpm -v 2.生成项目描述文件 package.j ...
- Terraform 系列-Terraform 项目的典型文件布局
系列文章 Terraform 系列文章 典型文件布局 - modules/ - services/ - webserver-cluster/ - examples/ - main.tf - outpu ...
- 全网最佳IoT命令行超级工具箱|帮你轻松解决百万物联网设备测试和联调
程序员离不开命令行,许多经典命令是每天必用的,比如ls 和 cd. 作为一个物联网开发和学习人员,IoT设备协议的测试联调是工作中很重要的一环!我有很多时刻都想拥有一个能集成常见物联网协议的客户端工具 ...
- pysimplegui之元素常用属性
常用元素参数 您将在几乎所有元素创建调用中看到的一些参数包括: key - 与 window[key].事件和返回值字典一起使用 工具提示tooltip - 将鼠标悬停在元素上,您将获得包含此文本的弹 ...
- 阿里云OSS服务 — 上传失败
问题重现 使用PicGo + 阿里云对象存储搭建图床,一直都能够正常使用,在没有修改任何配置的情况下,上传图片一直失败. 出现如下错误: StatusCodeError: 403 - "&l ...
- C# Kafka重置到最新的偏移量,即从指定的Partition订阅消息使用Assign方法
在使用Kafka的过程中,消费者断掉之后,再次开始消费时,消费者会从断掉时的位置重新开始消费. 场景再现:比如昨天消费者晚上断掉了,今天上午我们会发现kafka消费的数据不是最新的,而是昨天晚上的数据 ...
- XmlSerializer 反射类型xxx时出错,反射属性xxx时出错。
在使用XmlSerializer将类序列化成XML时出错,看到InnerException的message可以知道是这个receiver里有错误,进入这个类查看一下代码发现有重名的类 NodeId类修 ...