前言

本文的资料和图片均来自 \(\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\) 的第一个位置的搜索。

如果实在不会平衡树,vectorinserterase 操作也是个不错的选择呢!

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 算法的更多相关文章

  1. 「学习笔记」Min25筛

    「学习笔记」Min25筛 前言 周指导今天模拟赛五分钟秒第一题,十分钟说第二题是 \(\text{Min25}​\) 筛板子题,要不是第三题出题人数据范围给错了,周指导十五分钟就 \(\text{AK ...

  2. 「学习笔记」字符串基础:Hash,KMP与Trie

    「学习笔记」字符串基础:Hash,KMP与Trie 点击查看目录 目录 「学习笔记」字符串基础:Hash,KMP与Trie Hash 算法 代码 KMP 算法 前置知识:\(\text{Border} ...

  3. 「学习笔记」FFT 之优化——NTT

    目录 「学习笔记」FFT 之优化--NTT 前言 引入 快速数论变换--NTT 一些引申问题及解决方法 三模数 NTT 拆系数 FFT (MTT) 「学习笔记」FFT 之优化--NTT 前言 \(NT ...

  4. 「学习笔记」FFT 快速傅里叶变换

    目录 「学习笔记」FFT 快速傅里叶变换 啥是 FFT 呀?它可以干什么? 必备芝士 点值表示 复数 傅立叶正变换 傅里叶逆变换 FFT 的代码实现 还会有的 NTT 和三模数 NTT... 「学习笔 ...

  5. 「学习笔记」Treap

    「学习笔记」Treap 前言 什么是 Treap ? 二叉搜索树 (Binary Search Tree/Binary Sort Tree/BST) 基础定义 查找元素 插入元素 删除元素 查找后继 ...

  6. 「学习笔记」平衡树基础:Splay 和 Treap

    「学习笔记」平衡树基础:Splay 和 Treap 点击查看目录 目录 「学习笔记」平衡树基础:Splay 和 Treap 知识点 平衡树概述 Splay 旋转操作 Splay 操作 插入 \(x\) ...

  7. 「学习笔记」wqs二分/dp凸优化

    [学习笔记]wqs二分/DP凸优化 从一个经典问题谈起: 有一个长度为 \(n\) 的序列 \(a\),要求找出恰好 \(k\) 个不相交的连续子序列,使得这 \(k\) 个序列的和最大 \(1 \l ...

  8. 「学习笔记」ST表

    问题引入 先让我们看一个简单的问题,有N个元素,Q次操作,每次操作需要求出一段区间内的最大/小值. 这就是著名的RMQ问题. RMQ问题的解法有很多,如线段树.单调队列(某些情况下).ST表等.这里主 ...

  9. 「学习笔记」递推 & 递归

    引入 假设我们想计算 \(f(x) = x!\).除了简单的 for 循环,我们也可以使用递归. 递归是什么意思呢?我们可以把 \(f(x)\) 用 \(f(x - 1)\) 表示,即 \(f(x) ...

  10. 「学习笔记」min_25筛

    前置姿势 魔力筛 其实不看也没关系 用途和限制 在\(\mathrm{O}(\frac{n^{0.75}}{\log n})\)的时间内求出一个积性函数的前缀和. 所求的函数\(\mathbf f(x ...

随机推荐

  1. sqlmap的基本使用

    sqlmap的基本使用 一.注入 1.查询是否存在注入点 sqlmap -u "http://www.xx.com?id=x" --dbs 检测有哪些数据库 --current-d ...

  2. MarkdownStudy03JDK

    卸载JDK 删除jdk文件夹 删除配置环境(计算机>属性>高级>系统变量中的JAVA_HOME和Path) 删除JAVA_HOME环境变量 删除path环境变量 用dos窗口查看是否 ...

  3. 我没能实现始终在一个线程上运行 task

    前文我们总结了在使用常驻任务实现常驻线程时,应该注意的事项.但是我们最终没有提到如何在处理对于带有异步代码的办法.本篇将接受笔者对于该内容的总结. 如何识别当前代码跑在什么线程上 一切开始之前,我们先 ...

  4. python入门教程之二环境搭建

    环境搭建 1python解释器 当我们编写Python代码时,我们得到的是一个包含Python代码的以.py为扩展名的文本文件.要运行代码,就需要Python解释器去执行.py文件. 由于整个Pyth ...

  5. [软件设计&系统建模] Web软件通用能力模块

    0 基础工具 1 日志 2 权限 3 文件处理(下载/上传) 4 对象池 对象池 数据库连接池 线程池 5 微服务 服务网关 配置中心 注册中心 服务调用 服务熔断 健康检测 Actuator 6 缓 ...

  6. VScode连接GPU服务器进行深度学习

    VScode连接GPU服务器进行深度学习 ​ 最近用台式机跑一些小的深度学习项目,发现越来越慢了,由于一些原因,有时候需要我进行现场作业但是我的笔记本是轻薄本(Thinkpad YYDS)不带显卡,百 ...

  7. Longformer详解——从Self-Attention说开去

    1.Longformer的应用场景 为了理解Longformer的原理,我们最好首先从为何需要使用Longformer开始说起.(这里默认各位已经对Self Attention等基础知识有一定的了解) ...

  8. pdf 转 word

    目录 pdf 转 word 一.思路 二.软件安装下载 1. windows安装 2 certos7版本安装 3. Debian 版本安装 4. 安装字体 三.实现PDF转word文档 四.制作自己的 ...

  9. API网关:开源Apinto网关-上游服务篇(二)

    功能介绍 服务发现是一种分布式系统中的关键技术,它能够帮助应用程序动态地发现和访问依赖的服务实例,解决了服务实例分布在不同节点上的问题.通过服务发现,应用程序可以快速找到需要调用的服务实例的位置和元数 ...

  10. react中受控组件与非受控组件--

    非受控组件:随用随取 1 render() { 2 return ( 3 <div> 4 <h1>非受控组件</h1> 5 <form action=&quo ...