「学习笔记」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 ...
随机推荐
- Kingpin Private Browser - 隐私保护浏览器,隐身模式、广告拦截做你的私人浏览器
Kingpin Private Browser 是一个功能齐全的浏览器,隐身模式和广告拦截总是启用.它不会记住历史记录.密码或cookie.默认情况下,浏览器使用谷歌搜索(您可以在设置中将其更改为Du ...
- SQL Server底层架构技术对比
背景 数据库是信息化的基石,支撑着整个业务系统,发挥着非常重要的作用,被喻为"IT的心脏".因此,让数据库安全.稳定.高效地运行已经成为IT管理者必须要面对的问题.数据库在底层架构 ...
- HTTP TCP UDP WEBSOCKET
概念: TCP和UDP:传输层协议:(卡车) HTTP:应用层协议:(货物).HTTP(超文本传输协议)是利用TCP在两台电脑(通常是Web服务器和客户端)之间传输信息的协议.客户端使用Web浏览器发 ...
- 一连串div跟随鼠标移动
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 你需要知道的Symbols
著名symbol 著名symbol是一个在不同领域中都相同且未注册的symbol.如果我们要列出著名symbol,它们会是: Symbol.iterator Symbol.toStringTag Sy ...
- ACM-NEFUOJ-P239回文数
#include<bits/stdc++.h> using namespace std; int n,p[1000],len,p1[1000]; int f() { int i; for( ...
- python入门教程之二十三Python3 MySQL 数据库连接 - PyMySQL 驱动
MySQL 是最流行的关系型数据库管理系统,如果你不熟悉 MySQL,可以阅读我们的 MySQL 教程. 本章节我们为大家介绍使用 mysql-connector 来连接使用 MySQL, mysql ...
- 面试官:服务器最大可以创建多少个tcp连接以及端口并解释下你对文件句柄的理解
转载请注明出处: 1.最大可以创建多少个tcp连接 服务器最大可以创建多少个TCP连接取决于多个因素,例如服务器的硬件配置.网络带宽.操作系统设置等.一般来说,现代服务器的硬件资源和网络带宽都比较充足 ...
- 【Vue项目】尚品汇(五)Detail组件开发 实现轮播图和放大镜效果
1 基本准备工作 1.1 组件路由及数据准备 编写请求接口 api/index.js export const reqGetDetailInfo = (skuId ={}) => { retur ...
- MySQL中binlog备份脚本分享
关于MySQL的二进制日志(binlog),我们都知道二进制日志(binlog)非常重要,尤其当你需要point to point灾难恢复的时侯,所以我们要对其进行备份.关于二进制日志(binlog) ...