算法竞赛中的三分(ternary search)是一种用于在单峰函数(unimodal function)上找到最大值或最小值的搜索算法。与二分搜索(binary search)类似,三分搜索在每次迭代中将搜索空间分成三个部分,但三分搜索用于连续区间而不是离散区间。
算法思想
三分搜索的基本思想是在给定的区间内通过选择两个点将区间分成三个部分,并根据函数值的比较逐步缩小搜索范围。它主要适用于单峰函数,即在某一范围内函数有一个唯一的最大值或最小值。
算法步骤
假设我们在区间 [l,r] 上寻找函数 f(x) 的最大值。三分搜索的步骤如下:
- 初始化:设定初始区间 [l,r]。
- 计算两个分点:在区间 [l,r] 上选择两个点 m1 和 m2,通常选择方法为: m1=l+(r−l)/3, m2=r−(r−l)/3
- 比较函数值:计算 f(m1)和 f(m2),并进行比较:
- 如果 f(m1)<f(m2),说明最大值在区间 [m1,r]内,舍弃 [l,m1]。
- 如果 f(m1)>f(m2),说明最大值在区间 [l,m2]内,舍弃 [m2,r]。
- 如果 f(m1)=f(m2),则最大值在区间 [m1,m2]内,舍弃 [l,m1]和 [m2,r]。
- 更新区间:根据上述比较结果更新区间 [l,r]。
- 终止条件:重复步骤 2 至 4,直到区间长度小于某个预定的精度阈值 ϵ。
例题
如题,给出一个 N 次函数,保证在范围 [l, r] 内存在一点 x,使得 [l, x] 上单调增,[x, r] 上单调减。试求出 x 的值。
cpp
#include<bits/stdc++.h>
using namespace std;
const double EPS = 1e-7;
// 计算多项式在 x 处的值
double polynomial_value(const vector<double>& coefficients, double x) {
double value = 0.0;
for (const auto& coeff : coefficients) {
value = value * x + coeff;
}
return value;
}
// 三分搜索寻找极大值点
double ternary_search(const vector<double>& coefficients, double left, double right) {
while (right - left > EPS) {
double m1 = left + (right - left) / 3;
double m2 = right - (right - left) / 3;
double f1 = polynomial_value(coefficients, m1);
double f2 = polynomial_value(coefficients, m2);
if (f1 < f2) {
left = m1;
} else {
right = m2;
}
}
return (left + right) / 2;
}
int main() {
int N;
double l, r;
cin >> N >> l >> r;
vector<double> coefficients(N + 1);
for (int i = 0; i <= N; ++i) {
cin >> coefficients[i];
}
double x = ternary_search(coefficients, l, r);
cout << fixed << setprecision(6) << x << endl;
return 0;
}
这道交互算法题目要求找出一个缺失的刻度值 ( x ) (满足 ( 2 < x < 999 ))。对长度为 ( y ) 的物体进行测量时,尺子会根据 ( y ) 和 ( x ) 的关系给出不同的测量结果:
- 如果 ( y < x ),则尺子正确测量为 ( y )。
- 如果 ( y >= x ),则尺子错误测量为 ( y + 1 )。
你可以通过查询形如 ? a b
的指令,测量 ( a * b ) 矩形的面积,返回的面积是根据秘密尺子的测量结果计算的。题目中说明,通过最多 7 次查询找到 ( x ) 的值,查询返回的面积会受到 ( x ) 的影响。
每个测试用例独立,输入的第一行是测试用例的数量 ( t )( ( 1 <= t <= 1000 ))。正确输出找到的 ( x ) 值格式为 ! x
,输出错误或超过查询次数将导致程序失败。
cpp
#include <bits/stdc++.h>
using namespace std;
int query(int a, int b){
cout << "? " << a << " " << b << endl;
int res;
cin >> res;
return res;
}
int main(){
int task;
cin >> task;
while (task--){
int l = 1, r = 999;
while (l + 1 < r){
int midl = l + (r - l) / 3;
int midr = l + (r - l) * 2 / 3;
int res = query(midl, midr);
if (res == midl * midr){
l = midr;
}
else if (res == midl * (midr + 1)){
l = midl;
r = midr;
}
else{
// assert(res != (midl + 1) * (midr + 1));
r = midl;
}
}
cout << "! " << r << endl;
}
return 0;
}