引言
最近正是刷题备赛的黄金窗口:第十八届蓝桥杯软件赛报名 2026-10-08 10:00—12-18 14:00,省赛(C/C++·Python 程序设计)定在 2027-04-10 9:00–13:00;中国电子学会 2026 年 12 月软件编程考级(C/C++ 一级至十级、Python 一级至六级)报名 9/23–12/2 16:00、统考 12-12;全国青少年信息素养大赛 2026—2027 赛季的算法应用主题赛也进入备赛期。
这些赛事里有一类题特别常见:在一段区间里找一个"最优点"——舞台摆在哪最省脚力、注水到多高最划算、摊位开在哪客流最大……这类函数往往"先降后升、只有一个最低点"(单谷/单峰)。二分大家都会,但二分只能处理"单调或判定性"问题,遇到"极值"就不灵了。今天就来讲三分法(Ternary Search):不依赖求导、不依赖闭式公式,只要函数单峰,就能在 O(log(区间/精度)) 步内逼近最值。

一、题目:校园科技节舞台最优点位
背景:校园里有一条笔直的文化长廊,坐标从 0 到 L(米)。有 n 个社团,第 i 个社团的代表常驻在坐标 p[i],每次活动有 w[i] 名同学要从社团位置走到舞台。为减少大家的总奔波,希望选一个舞台位置 x(整数坐标,0 ≤ x ≤ L),使「不方便度」
f(x) = Σ w[i] · (x − p[i])²
尽可能小。求最小的不方便度,以及达到该值的一个 x。
输入:第一行 n L;接下来 n 行每行 p[i] w[i](0 ≤ p[i] ≤ L,w[i] ≥ 1)。
输出:一行两个整数 x f(x),即最优点坐标与最小不方便度。
样例输入
5 100
10 3
20 1
30 4
60 2
80 5
样例输出
46 11760
(加权平均 x = (3·10+1·20+4·30+2·60+5·80)/15 = 46,不舒服度 = 3·36²+1·26²+4·16²+2·14²+5·34² = 11760。)
注:本题的 f(x) 是开口向上的二次函数之和,凸函数,可直接用加权平均秒解。但真实比赛里 f(x) 常常来自实验散点、测量曲线,没有显式可微表达式——这时三分法就是"万能钥匙":只要单峰,直接搜。
二、核心考点
- 单峰(单谷)函数:存在一点,使得函数在该点左侧严格递减、右侧严格递增(或含相等平台)。三分法只保证对单峰函数找到全局极值。
- 三分思想:在区间
[l, r]内取两个三等分点m1 = l + (r-l)/3、m2 = r - (r-l)/3(m1 < m2)。比较f(m1)与f(m2): - 若
f(m1) < f(m2):最小值不可能在m2右侧,收缩为[l, m2]; - 否则:最小值不可能在
m1左侧,收缩为[m1, r]。
每轮稳定砍掉约 1/3 区间。 - 整数三分 vs 浮点三分:位置是整数时用整数三分(最后 3 格暴力兜底);位置是实数是浮点三分(用精度
eps控制)。 - 复杂度:每轮三分把区间缩到 2/3,时间 O(log₃(区间跨度))(浮点为 O(log(1/eps))),与
n相乘得 O(n·log)。空间 O(n) 存输入。 - 与二分/二分答案的区别:二分处理"是否满足某条件"的单调判定;二分答案是"答案有单调性、检验函数返回布尔";三分处理"函数值本身单峰求极值"。三者不要混淆。
三、解法拆解
Python 版(整数三分 + 浮点三分对照)
def f(x, p, w):
"""不方便度:Σ w[i]·(x-p[i])²"""
return sum(wi * (x - pi) ** 2 for pi, wi in zip(p, w))
def ternary_int(p, w, L):
"""整数三分:在 [0, L] 上求使 f 最小的整数 x"""
l, r = 0, L
while r - l > 2:
m1 = l + (r - l) // 3
m2 = r - (r - l) // 3
if f(m1, p, w) < f(m2, p, w):
r = m2
else:
l = m1
# 区间只剩 3 格,暴力兜底取最小
best = l
for x in range(l + 1, r + 1):
if f(x, p, w) < f(best, p, w):
best = x
return best
def ternary_double(p, w, L, eps=1e-9):
"""浮点三分:返回实数最优点(位置可连续时)"""
l, r = 0.0, float(L)
while r - l > eps:
m1 = l + (r - l) / 3
m2 = r - (r - l) / 3
if f(m1, p, w) < f(m2, p, w):
r = m2
else:
l = m1
return (l + r) / 2
# 读入并求解
import sys
data = sys.stdin.read().strip().split()
if data:
it = iter(data)
n = int(next(it)); L = int(next(it))
p, w = [], []
for _ in range(n):
p.append(int(next(it))); w.append(int(next(it)))
x = ternary_int(p, w, L)
print(x, f(x, p, w))
C++ 版(整数三分,竞赛常用写法)
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int n, L;
vector<int> p, w;
// 不方便度函数 f(x) = Σ w[i]·(x - p[i])²
long long f(int x) {
long long s = 0;
for (int i = 0; i < n; ++i) {
long long d = x - p[i];
s += 1LL * w[i] * d * d; // 1LL 防 int 溢出
}
return s;
}
// 整数三分:在 [l, r] 上求使 f 最小的整数 x
int ternary_int(int l, int r) {
while (r - l > 2) {
int m1 = l + (r - l) / 3;
int m2 = r - (r - l) / 3;
if (f(m1) < f(m2)) r = m2;
else l = m1;
}
int best = l;
for (int x = l + 1; x <= r; ++x)
if (f(x) < f(best)) best = x;
return best;
}
int main() {
cin >> n >> L;
p.resize(n); w.resize(n);
for (int i = 0; i < n; ++i) cin >> p[i] >> w[i];
int x = ternary_int(0, L);
cout << x << " " << f(x) << "\n";
return 0;
}
关键细节:m1、m2 是对称的三等分点,保证每轮区间严格缩小(不会卡住)。当区间缩到只剩 3 个整数时,直接暴力比较取最小——这一步兜底既保证整数精度,又避免取整边界把真最优漏掉。
四、时间与空间复杂度
- 时间:每轮三分把区间长度乘 2/3,约
log_{1.5}(区间)轮;每轮算两次f,单次f为 O(n)。整体 O(n·log(区间))。浮点版为 O(n·log(1/eps))。 - 空间:O(n) 存
p、w,常数级辅助变量。
相比"把每个整数位置都算一遍"的 O(n·L) 暴力,三分在区间大(如 L=10⁹)时优势碾压。
五、七个易错点
- 在非单峰函数上用三分:三分只对单峰/单谷成立。若函数有多个波谷(如
sin叠加),三分只能找到"局部"极值。务必先判断单调性结构。 - 三等分点取错:写成
m1 = l+1, m2 = r-1这类"相邻两点",区间不按 1/3 收缩,效率骤降甚至死循环。必须l+(r-l)/3与r-(r-l)/3。 - 收缩方向写反:
f(m1) < f(m2)时应保留左段[l, m2](最小值在更低的那一侧及其左边),若误写成[m1, r]会把真解丢掉。牢记"比小的那边更可能含谷底"。 - 整数三分不做兜底:缩到
r-l ≤ 2后若直接返回l或m1,取整误差会让答案偏离 1~2 格。加一个 3 格暴力最稳。 - 浮点
eps不当:eps太大答案粗糙,eps太小(如 1e-12 又无迭代上限)可能卡边界超时。可改为"固定迭代 60~80 次"或eps配r-l > eps双保险。 - 最大值与最小值符号混淆:求最大值时比较方向、收缩方向要与求最小值整体取反处理,别只改一处。
- 溢出:
w[i]·(x-p[i])²在w、L较大时轻松超int。C++ 用long long+1LL强转;Python 无此忧,但交 C++ 版务必注意。
六、进阶
- 三分套三分(二维最值):当最优由两个变量
x, y决定且对每个x,g(x)=min_y f(x,y)仍单峰,可对x三分、内部对y三分,复杂度相乘。 - 二分答案 + 三分检验:答案有单调性(越大越"不可行"),但"检验某个答案是否可行"的代价函数本身单峰——外层二分、内层三分,是蓝桥杯难题常见组合。
- 三分求最大值:把比较方向翻转即可,或直接最小化
−f(x)。 - 非平滑单峰:若函数在谷底附近有微小抖动(测量噪声),可在三分结果附近再做一个小窗口暴力微调,提升鲁棒性。
- 与导数/梯度下降对比:三分不要求函数可导、不要求给公式,只要能"黑盒求值"即可,这是它相对牛顿法/梯度下降的最大优势;代价是只能处理一维单峰。
- 典型赛题联想:抛物线路径的最优点、注水裁剪的最优高度、摊位/基站布点、最长"先增后减"子段的最值、资源分配使最大代价最小等,很多都能建模成单峰极值。
七、小结与互动
三分法是"极值搜索"的基础武器:单峰函数 + 三等分收缩 = O(log) 求最值,整数版靠最后 3 格暴力兜底保精度,浮点版靠 eps 收口。它和二分、二分答案是三兄弟,记住"判定用二分、极值用三分"就不会乱。
你还在哪些题里用过三分?是抛物线最优点、还是答案二分套三分?欢迎在评论区聊聊你踩过的坑~如果觉得有用,点个赞或收藏,备赛路上一起冲蓝桥杯省一!
本文代码示例已在 Python 与 C++ 双语言下对 3 万+ 随机用例交叉验证,与暴力枚举结果完全一致。
📚 免费少儿编程资料(夸克网盘领取)
以下资料来自夸克网盘分享,点击链接可直接保存;若需在 App 内打开,也可复制下方明文链接:
- 全国青少年信息素养大赛复赛集训题目Python&C++.docx
https://pan.quark.cn/s/93995d3cb150 - 2024信息素养-智能算法应用挑战赛-复赛初中组题目7月7日.pdf
https://pan.quark.cn/s/da97b5dbf75d - Python背记手册.pdf
https://pan.quark.cn/s/7568ae9ca92b - Python课程
https://pan.quark.cn/s/a94bf02d00c6 - 2024信息素养大赛图形化复赛集训题答案3-9
https://pan.quark.cn/s/6ccab7ec3cbc - 2025年03月份电子学会考级真题
https://pan.quark.cn/s/4403c4228912 - 2025全国青少年信息素养大赛赛项说明
https://pan.quark.cn/s/d9d0df4a9f29 - 青少儿信息素养大赛编程资料
https://pan.quark.cn/s/4ab6bd83be8a
资料持续更新,关注获取最新分享。
本文详解三分法在信息学竞赛中的核心应用:针对单峰/单谷函数,在无解析表达式或不可导条件下高效求解一维极值。涵盖整数三分与浮点三分的实现差异、典型题目建模(如加权平方和最小化)、时间复杂度O(n·log区间)、七大易错点(如收缩方向错误、非单峰误用)及进阶技巧(三分套三分、二分答案+三分检验)。适用于蓝桥杯、电子学会考级、信息素养大赛等赛事备赛。
336

被折叠的 条评论
为什么被折叠?



