三分算法学习笔记

news/2025/2/22 17:06:28/文章来源:https://www.cnblogs.com/wusixuan/p/18731047

0.概念

二分算法,顾名思义,就是每一次将区间分成 2 个一样的区间。

三分算法,同样顾名思义,就是每一次将区间分成 3 个一样的区间。

三分算法主要用来求解一些关于单调函数的问题。 而且考试中有一定的几率考到。


二分算法主要用来求一些简单的最值问题,例如在一些一次函数上。

三分算法主要用来求一些凹函数和凸函数的最值问题,典型地,在一些二次函数上,甚至是高次函数。

凸函数的定义:

  • 学术描述:对此函数进行二次求导,最后的值恒 \(<0\)

  • 人话描述:先上升后下降的函数。

凹函数的定义就是先下降后上升的函数。

当然,函数的形状各种各样,以上的定义不一定是完全正确的。

只需要记住,三分的函数一定是单峰函数。

而且大部分三分都是基于浮点数的。

1.思路

强烈推介学习二分的思想之后再来看这部分。

一开始,仿照二分的思想,我们需要确定一个区间,使得最值一定在这个区间里面。

设区间的左端点为 \(L\),区间的右端点为 \(R\)

我们在区间里面随便取两个点 \(lmid,rmid\) 使 \(lmid < rmid\),然后获取并比较函数在 \(lmid,rmid\) 的位置的值,记为 \(val_{lmid},val_{rmid}\)文章以后会继续沿用此定义)。

设我们需要三分的函数是凸函数,如果是凹函数则可以看为反过来。

毕竟我们需要做的题目要么就是凸函数,要么就是凹函数,一定要在思考三分做法的前面思考函数的形状

有以下三种情况:

第一种情况:\(val_{lmid} < val_{rmid}\)

则若峰值出现在 \(lmid\) 左边的某个位置,一定有一点比其更大(这里有一个现成的例子:\(rmid\)),矛盾。

所以,遇到这种情况时,我们就可以排除掉 \(lmid\) 左边的区间了。

第二种情况:\(val_{rmid} > val_{lmid}\)

则若峰值出现在 \(rmid\) 右边的某个位置,一定有一点更大(\(lmid\)),矛盾。

所以,遇到这种情况时,我们就可以排除掉 \(rmid\) 右边的区间了。

第三种情况:\(val_{lmid} = val_{rmid}\)

这种情况可以直接使用 else 判到第二种情况内。

得到新的 \(L,R\) 之后,再次选 \(lmid,rmid\) 即可。

这不是真正的三分,只是其中的一部分。三分是基于其进行了优化。


这时候,我们观察到一个区间被分为了三个部分\([L,lmid]\)\([lmid,rmid]\)\([rmid,R]\)

而不难注意到每一次都是第一个或者第三个子区间被排除。 因此引出三分的两个优化想法:

优化1:将以上三个区间的长度都尽量逼近至 \(\frac{1}{3} (R-L+1)\)
优化2:将中间的区间的长度留的极小,无限趋近但不等于 \(0\)。将留下来的继续平分给其他两个区间。

执行这两个优化之后,三分的复杂度可以看成 \(O(\log n)\),其中 \(n = R-L+1\)

但是因为 \(\log\) 的底数不管是什么值,答案总不会相差太大。所以加上那个优化都一样。

2.代码

浮点数三分:

// 寻找凸函数在[l, r]区间内的最值,eps 表示精度
double ternary_search_max(double l, double r, double eps) {// 当区间长度大于精度要求时持续三分while (r - l > eps) {// 得到两个中间点double lmid = l + (r - l) / 3.0;double rmid = r - (r - l) / 3.0;// 比较两个中间点的函数值,缩小搜索范围if (cal(lmid) < cal(rmid))l = lmid;  // 排除左半区间elser = rmid; // 排除右半区间}return r;//返回右端点
}// 寻找凹函数在[l, r]区间内的最值,eps 表示精度
double ternary_search_min(double l, double r, double eps) {// 终止条件与最大值搜索相同while (r - l > eps) {double lmid = l + (r - l) / 3.0;double rmid = r - (r - l) / 3.0;// 比较逻辑与最大值搜索相反if (cal(lmid) > cal(rmid))l = lmid;  // 最小值在右半区间elser = rmid; // 最小值在左半区间}return l;//返回左端点
}//实际上返回 l 还是 r 已经不重要了,因为此时 l 和 r 相差不大

整数三分:

// 寻找凸函数在[l, r]区间内的最值
int ternary_search_max(int l, int r) {// 当区间长度大于 3 时持续三分while (r - l > 3) {// 得到两个中间点int lmid = l + (r - l) / 3;int rmid = r - (r - l) / 3;// 比较两个中间点的函数值,缩小搜索范围if (cal(lmid) < cal(rmid))l = lmid;// 排除左半区间elser = rmid;// 排除右半区间}int ans = l;for (int i = l; i <= r; ++i)//这里直接暴力算,因为实际判断太复杂了if (cal(ans) < cal(i))ans = i;return ans;//返回结果
}

3.难点

乍一看,三分的代码以及思路实际上并不难,但是为什么三分的题目经常出现蓝紫题呢?

实际上,三分的难点在于判断函数是一个单峰函数。

面对一个问题,有数学功底的同学都会不难想到先将问题的值抽象成一个函数。

但是大部分人往往到这时就不会做了,反而会因为函数的其他性质被带偏:上贪心、动态规划……从而陷入深潭,拼尽全力无法做出。

但如果这时有人告诉你这是一个单峰函数,你会不会恍然大悟?


有很多正向得出单峰函数的方法。

  • 第一种,求二阶导数。

但是如果函数过于复杂,而且分段也无法求出,这样就很难做出问题了。

  • 第二种,抽点计数

因为在 NOI 系列竞赛中无法使用几何画板,所以我们就尝试写一个暴力,抽几个点绘制函数图像,看一下到底像不像单峰函数。

  • 第三种,证明

根据数学知识,凸函数主要满足以下公式:

\[\forall x_1\, x_2\in C,x_1\le x_2,\frac{1}{2}(f(x_1)+f(x_2))<f(\frac{1}{2}(x_1+x_2)) \]

当上述公式成立时,这个函数就是凸函数。

\[\forall x_1,x_2\in C,x_1\le x_2,\frac{1}{2}(f(x_1)+f(x_2))>f(\frac{1}{2}(x_1+x_2)) \]

成立时,这个函数就是凹(也称下凸)函数。

  • 第四种,求出峰值并证明

我们还可以对于函数找出一峰点 \(x\),并证明,\(x\) 左边开始下降 or 上升,右边上升 or 下降。

第一种方法较少用,一般以第四种和第二种为主。

因此,想要将三分题目做的得心应手,必须要有很扎实的数学功底。

4.练习

P1883 【模板】三分 | 函数

因为同时考虑 \(100\) 个函数的值过于困难了,所以我们考虑只有两个函数 \(f(x)\)\(g(x)\),并设 \(h(x)=\max(f(x),g(x))\)

很显然,由于 \(a\ge 0\),则 \(f,g\) 为下凸函数(即凹函数)。我们需要证明 \(h\) 也为下凸函数,即需要证明

\[\forall x_1,x_2\in C,x_1\le x_2,\frac{1}{2}(h(x_1)+h(x_2))>h(\frac{1}{2}(x_1+x_2)) \]

这个式子恒成立。


首先根据 \(h\) 的定义,将 \(f(x)\)\(g(x)\) 往里代换:

\[\frac{1}{2}(\max(f(x_1),g(x_1))+\max(f(x_2),g(x_2)))>\max(f(\frac{x_1+x_2}{2}),g(\frac{x_1+x_2}{2})) \]

因为 \(f,g\) 都为下凸函数,则可得:

\[\left\{\begin{matrix} \frac{1}{2}(f(x_1)+f(x_2))>f(\frac{1}{2}(x_1+x_2))\\\\\frac{1}{2}(g(x_1)+g(x_2))>g(\frac{1}{2}(x_1+x_2)) \end{matrix}\right. \]

将两个式子一比较,直接证毕。

考虑扩展到更多函数的情况:将得出来的下凸函数 \(h\) 再次与另一个下凸函数 \(k\) 进行 \(\max\) 合并,同理得到一个新的下凸函数;然后再次进行合并……

因此,可以知道:无论有多少个函数,只要都是下凸的,进行 \(\max\) 合并之后的出来的函数也一定下凸。

#include <bits/stdc++.h>
#define int long long
using namespace std;
int t, n;
const int N = 10010;
const double eps = 1e-9;//eps在不爆精度的情况下尽量小
int a[N], b[N], c[N];double cal(double x) {//计算函数值double ans = -1e18;for (int i = 1; i <= n; i++)ans = max(ans, x * a[i] * x + b[i] * x + c[i]);return ans;
}signed main() {cin >> t;while (t--) {cin >> n;for (int i = 1; i <= n; i++)cin >> a[i] >> b[i] >> c[i];double l = 0, r = 1000;//题目中规定了范围while (r - l > eps) {double lmid = l + (r - l) / 3.0;double rmid = r - (r - l) / 3.0;if (cal(lmid) > cal(rmid))l = lmid;elser = rmid;}printf("%.4lf\n", cal(l));}return 0;
}

CF201B Guess That Car!

简化描述

给出一个 \(n\times m\) 的方阵(行和列数从 \(1\) 开始),每一个格子的半径为 \(4\)(不要问我是从哪里来的,要问就去看样例解释)。每一个格子 \((i,j)\)中心都有一个数 \(C_{i,j}\)

你需要确定一个格点,算出这个格点到每一个格子 \((i,j)\)中心点欧几里得距离 \(d_{i,j}\),并最小化该式子:

\[\sum_{i=1}^{n} \sum_{j=1}^{m}C_{i,j}\times d_{i,j}^2 \]

思路

此题细节较多,又是中心又是格点,较难实现。注意要仔细阅读题目中的输出格式。

不妨按照输出格式设格点的位置为 \((x,y)\)

根据中心点的性质,可以算出 \((i,j)\) 格子的中心点的位置为 \((i-0.5,j-0.5)\)

不妨对 \(d_{i,j}\) 进行拆解:

\[d_{i,j}=\sqrt ((x-i+0.5)\times 4)^2+((y-j+0.5)\times 4)^2 \]

所以 \(d_{i,j}^2=((x-i+0.5)\times 4)^2+((y-j+0.5)\times 4)^2\)

因为这时候我们可以分 \(x,y\) 分别计算,直接针对横坐标和纵坐标跑三分即可。

而显然这是两个下凸函数。

#include <bits/stdc++.h>
#define int long long
using namespace std;
int n, m;
const int N = 1010;
int c[N][N];int cal(int x, int val) {//val=1表示行,val=2表示列,下同int ans = 0;for (int i = 1; i <= n; i++)for (int j = 1; j <= m; j++) {if (val == 1)ans += c[i][j] * ((x - i) * 4 + 2) * ((x - i) * 4 + 2);elseans += c[i][j] * ((x - j) * 4 + 2) * ((x - j) * 4 + 2);//套用公式}return ans;
}int smin(int l, int r, int val) {//对ternary_search_max的一些小改版while (r - l > 3) {int lmid = l + (r - l) / 3;int rmid = r - (r - l) / 3;if (cal(lmid, val) > cal(rmid, val))l = lmid;elser = rmid;}int ans = l;for (int i = l; i <= r; i++)if (cal(ans, val) > cal(i, val))ans = i;return ans;
}signed main() {scanf("%lld%lld", &n, &m);//注意使用正确的读入方式for (int i = 1; i <= n; i++)for (int j = 1; j <= m; j++)scanf("%lld", &c[i][j]);int lans = smin(0, n, 1), rans = smin(0, m, 2);printf("%lld\n%lld %lld\n", cal(lans, 1) + cal(rans, 2), lans, rans);return 0;
}

5.凸函数的性质

经过一些的题目练习之后,我们可以总结出凸函数的一些性质:

  • \(f(x)\) 是定义在凸集 \(S\) 上的凸函数,对于任意非负数 \(c\ge 0\),函数 \(c f(x)\) 也是凸函数。
  • \(f(x),g(x)\) 都是定义在凸集 \(S\) 上的凸函数,则函数 \(f(x)+g(x)\) 也是凸函数。
  • \(f(x),g(x)\) 都是定义在凸集 \(S\) 上的凸函数,且 \(g\) 值递增,那么 \(h(x)=g(f(x))\) 凸函数。
  • \(f(x)\) 为定义在凸集 \(S\) 上的凸函数,则对于任意实数 \(c\),集合 \(S_c=\{x|x\in S,f(x)\le c\}\) 是凸集。
  • \(f(x)\) 为定义在凸集 \(S\) 上的凸函数,则它的任意一个极小点就是它在 \(S\) 上的全局极小点,而且所有极小点的集合是凸集。
  • 一个上升函数和一个下降函数取 \(\min\)\(\max\) 都会重新形成一个单峰函数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/888061.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2025寒假总结2

前言 这是第二篇总结,考虑到与前一篇的时间临近,所以不展开叙述做过的事情,此篇文章重点写关于最近的收获、现在的知识体系以及后面的计划。 Part 1 记录 这部分大概讲一下寒假做过的事,大体按照时间线展开。 首先 16 号是竞赛生大会,晚上回来后补完北京的游记,看了一会数…

为 Power Automate 注册 Adobe PDF Services

前言最近,再测试如何将HTML转换成PDF,然后发现Adobe有一个免费的操作可以用,好开心,赶紧注册一下。正文1.先注册一个账号,然后登录到Adobe Developer注册链接:https://www.adobe.com/go/getstarted_powerautomate2.这就是新建好的,然后密码要获取一下,如下图:3.然后,…

记一次golang项目context引发的进程OOM故障

之前写过一篇一种基于etcd实践节点自动故障转移的思路, 程序经历过一次线上进程内存持续上涨终OOOM的小事故, 本次技术复盘导致本次内存泄露的完整起因。 提炼代码: 业务函数etcdWatchLoop: 基于etcd的Watch机制持续监听/foo前缀键值对的变更; 收到Watch信道的变更消息,就…

记一次golang项目context引发的OOM故障

之前写过一篇一种基于etcd实践节点自动故障转移的思路, 程序经历过一次线上进程内存持续上涨终OOOM的小事故, 本次技术复盘导致本次内存泄露的完整起因。 提炼代码: 业务函数etcdWatchLoop: 基于etcd的Watch机制持续监听/foo前缀键值对的变更; 收到Watch信道的变更消息,就…

R语言LCMM多维度潜在类别模型流行病学研究:LCA、MM方法分析纵向数据

全文代码数据:https://tecdat.cn/?p=39710 原文出处:拓端数据部落公众号 在数据分析领域,当我们面对一组数据时,通常会有已知的分组情况,比如不同的治疗组、性别组或种族组等。然而,数据中还可能存在未被观测到的分组,例如素食者与非素食者、经常锻炼者与不锻炼者,或…

TensorFlow域对抗训练DANN神经网络分析MNIST与Blobs数据集梯度反转层提升目标域适应能力可视化

全文链接:https://tecdat.cn/?p=39656 原文出处:拓端数据部落公众号 本文围绕基于TensorFlow实现的神经网络对抗训练域适应方法展开研究。详细介绍了梯度反转层的原理与实现,通过MNIST和Blobs等数据集进行实验,对比了不同训练方式(仅源域训练、域对抗训练等)下的分类性能…

【专题】2025年我国机器人产业发展形势展望:人形机器人量产及商业化关键挑战报告汇总PDF洞察(附原数据表)

原文链接:https://tecdat.cn/?p=39668 机器人已广泛融入我们生活的方方面面。在工业领域,它们宛如不知疲倦的工匠,精准地完成打磨、焊接等精细工作,极大提升了生产效率和产品质量;在日常生活里,它们是贴心的助手,扫地机器人默默清扫房间,陪伴机器人给予老人孩子温暖陪…

vba主动着色

原来的条件格式效率太低,改为主动方式着色 Sub SetColor() On Error Resume Next Dim hang As Integer 行数 Dim lie As Integer Dim IsBuy As Boolean Dim IsSell As Boolean hang = ActiveSheet.UsedRange.Rows.Count With ActiveSheet …

2025省选模拟13

2025省选模拟13\(T1\) P1025. Easy Problem \(40pts\)部分分\(40pts\)设 \(f_{i,j}\) 表示 \(p_{3j}=i\) 时 \([1,i]\) 对答案的贡献,状态转移方程为 \(f_{i,j}=\max\limits_{k=3(j-1)}^{i-3} \{ f_{k,j-1}+w(k+1,i) \}\) ,其中 \(w(k+1,i)\) 表示 \([k+1,i]\) 的次大值。 设…

installerX还你一个清爽的安装

相信大家都有被手机自带的软件安装器折磨的情况,各种禁止安装,这种验证和识别,不开启安全模式和开了没区别,针对这种情况有没有什么办法绕过呢? 我们可以使用开源软件installerX,这款软件使用拥有这类原生的安装体验,安装速度也不差,并且简洁高效,还可以进行降级安装。…

[Paper Writting] 论文画图指南

目录Motivation方法概念图新老对比类方法简图类实物示意图效果示意图Architecture Motivation 方法概念图 HPT新老对比类 OSXMOTRUniADMulti-modal 3D Human Pose Estimation方法简图类 MoCoconformerBEVFormerDETRDriveVLM实物示意图 emg2pose效果示意图 umetracktransmvshoid…

不到24小时,AOne让全员用上DeepSeek的秘诀是……

DeepSeek引发新一轮AI浪潮,面对企业数字化智能升级与数据安全红线的急迫需求,IT负责人的压力山大!如何在24小时内实现全员AI落地,同时为后续安全部署铺平道路?Step1:一键开启全员智能时代 基于国产大模型领军者DeepSeek(671B满血版&70B版),天翼云AOne搭载智能引擎…