题面:
P1883 【模板】三分 | 函数
题目描述
给定 n n n 个二次函数 f 1 ( x ) , f 2 ( x ) , … , f n ( x ) f_1(x),f_2(x),\dots,f_n(x) f1(x),f2(x),…,fn(x)(均形如 a x 2 + b x + c ax^2+bx+c ax2+bx+c),设 F ( x ) = max { f 1 ( x ) , f 2 ( x ) , . . . , f n ( x ) } F(x)=\max\{f_1(x),f_2(x),...,f_n(x)\} F(x)=max{f1(x),f2(x),...,fn(x)},求 F ( x ) F(x) F(x) 在区间 [ 0 , 1000 ] [0,1000] [0,1000] 上的最小值。
输入格式
输入第一行为正整数 T T T,表示有 T T T 组数据。
每组数据第一行一个正整数 n n n,接着 n n n 行,每行 3 3 3 个整数 a , b , c a,b,c a,b,c,用来表示每个二次函数的 3 3 3 个系数,注意二次函数有可能退化成一次。
输出格式
每组数据输出一行,表示 F ( x ) F(x) F(x) 的在区间 [ 0 , 1000 ] [0,1000] [0,1000] 上的最小值。答案精确到小数点后四位,四舍五入。
输入输出样例 #1
输入 #1
2
1
2 0 0
2
2 0 0
2 -4 2
输出 #1
0.0000
0.5000
说明/提示
对于 50 % 50\% 50% 的数据, n ≤ 100 n\le 100 n≤100。
对于 100 % 100\% 100% 的数据, T < 10 T<10 T<10, n ≤ 1 0 4 \ n\le 10^4 n≤104, 0 ≤ a ≤ 100 0\le a\le 100 0≤a≤100, ∣ b ∣ ≤ 5 × 1 0 3 |b| \le 5\times 10^3 ∣b∣≤5×103, ∣ c ∣ ≤ 5 × 1 0 3 |c| \le 5\times 10^3 ∣c∣≤5×103。
S o l u t i o n Solution Solution
首先为了解决这个问题,我们需要引入一个定义:单峰函数。
定义
设有一定义在实数集合 D D D 上的实值函数 f ( x ) f(x) f(x) ,若 ∃ x 0 ∈ D \exist x_0 \in D ∃x0∈D ,使得 ∀ x 1 , x 2 ∈ D \forall x_1,x_2 \in D ∀x1,x2∈D ,若 x 1 < x 2 < x 0 x_1<x_2<x_0 x1<x2<x0 ,有
f ( x 1 ) ≤ f ( x 2 ) f(x_1) \le f(x_2) f(x1)≤f(x2)
若 x 0 > x 1 > x 2 x_0>x_1>x_2 x0>x1>x2 ,有
f ( x 1 ) ≥ f ( x 2 ) f(x_1) \ge f(x_2) f(x1)≥f(x2)
则称 f ( x ) f(x) f(x) 为定义在 D D D 上的单峰函数, x 0 x_0 x0 称为 f ( x ) f(x) f(x) 在 D D D 上的峰值点。
三分法
有的时候,我们需要求解一个单峰函数的峰值点,则此时我们引入三分法来解决这个问题。
什么是三分法?回顾二分法的定义,我们将整个区间分成两个部分,然后通过中值的性质来不断缩小区间,进而迫近得到答案或近似解。
那么三分法也是一样的道理,我们通过将整个区间分成三个部分,具体的,设有当前区间 [ l , r ] [l,r] [l,r] , m 1 = l + r − l 3 , m 2 = r − r − l 3 m_1=l+ \dfrac {r-l}{3},m_2=r-\dfrac {r-l}{3} m1=l+3r−l,m2=r−3r−l ,分别计算 f ( m 1 ) , f ( m 2 ) f(m_1),f(m_2) f(m1),f(m2)
若有 f ( m 1 ) < f ( m 2 ) f(m_1) < f(m_2) f(m1)<f(m2) ,则回顾单峰函数的定义,其峰值点必然在 [ m 1 , r ] [m_1,r] [m1,r] 中,于是将区间缩小为 [ m 1 , r ] [m_1,r] [m1,r]
同样的,若有 f ( m 1 ) > f ( m 2 ) f(m_1)>f(m_2) f(m1)>f(m2) ,其峰值点必然在 [ l , m 2 ] [l,m_2] [l,m2] 中,于是将区间缩小为 [ l , m 2 ] [l,m_2] [l,m2]
综上,我们通过这样反复缩小,最终可以得到一个较小的范围,于是可以通过暴力枚举或直接估计误差去近似解得到答案。
应用
那么讲了那么多,本题怎么使用三分法求解呢?你可能会说,这要求的不是最小值吗,单峰函数不是最大值吗,但其实是一样的,本题这个函数是单谷函数,将单峰函数的定义稍作变形,即由极大值点变为极小值点即可。
我们来证明本题的函数为单谷函数,使用数学归纳法。
首先本题函数可表示为:
∑ i = 1 n m a x ( f i ( x ) ) \sum_{i=1}^nmax(f_i(x)) i=1∑nmax(fi(x))
对于 n = 1 n=1 n=1 的情况:
若 a ≠ 0 a \not= 0 a=0 ,则给出函数的形式是一个二次函数,显然其有极小值点 x 0 = − b a x_0= - \dfrac {b}{a} x0=−ab ,在任意区间 D D D ,其有且仅有一个极小值。
若 a = 0 a=0 a=0 则为一个一次函数,其在 R \mathbb {R} R 上单调递增或单调递减,显然其在任意区间 D D D 上也仅有一个极小值。
故此时 ∑ i = 1 n m a x ( f i ( x ) ) = f 1 ( x ) \sum_{i=1}^{n}max(f_i(x))=f_1(x) ∑i=1nmax(fi(x))=f1(x) ,其为单谷函数,即 n = 1 n=1 n=1 时命题成立。
下证对于 ∑ i = 1 k m a x ( f i ( x ) ) \sum_{i=1}^{k}max(f_i(x)) ∑i=1kmax(fi(x)) 为单谷函数成立时, ∑ i = 1 k + 1 m a x ( f i ( x ) ) \sum_{i=1}^{k+1}max(f_i(x)) ∑i=1k+1max(fi(x)) 也为单谷函数。
设 F ( x ) = ∑ i = 1 k m a x ( f i ( x ) ) F(x)=\sum_{i=1}^{k}max(f_i(x)) F(x)=∑i=1kmax(fi(x)) ,则 ∑ i = 1 k + 1 m a x ( f i ( x ) ) = m a x ( F ( x ) , f k + 1 ( x ) ) \sum_{i=1}^{k+1}max(f_i(x))=max(F(x),f_{k+1}(x)) ∑i=1k+1max(fi(x))=max(F(x),fk+1(x))
即证明对于两个单谷函数 f ( x ) , g ( x ) f(x),g(x) f(x),g(x) , m a x ( f ( x ) , g ( x ) ) max(f(x),g(x)) max(f(x),g(x)) 也为单谷函数。
我们先证明一个引理:设 f , g f,g f,g 在闭区间 D D D 上均为单谷函数,取任意极小值点 x 1 ( 对 f ) x_1(对f) x1(对f) 与 x 2 ( 对 g ) x_2(对g) x2(对g) ,且假设 x 1 ≤ x 2 x_1 \le x_2 x1≤x2 ,令
A = { x ∈ D : f ( x ) ≤ g ( x ) } , B = { x ∈ D : f ( x ) ≥ g ( x ) } A=\{x \in D:f(x) \le g(x)\}, B=\{x \in D:f(x) \ge g(x)\} A={x∈D:f(x)≤g(x)},B={x∈D:f(x)≥g(x)}
则 A , B A,B A,B 都是闭区间(可能退化为点),并且 D = A ∪ B D=A \cup B D=A∪B ,从而 H ( x ) = m a x ( f ( x ) , g ( x ) ) H(x)=max(f(x),g(x)) H(x)=max(f(x),g(x)) 在 D D D 左端由某一函数主导,右端由某一函数主导,中间至多切换一次主导者,故 H ( x ) H(x) H(x) 为单谷函数。
下面我们来证明这个引理。
证明:
先说明为什么 A A A 必为区间:
由于 x 1 ≤ x 2 x_1 \le x_2 x1≤x2 ,将区间 D D D 分为三段:
·左段 L = D ∩ ( − ∞ , x 1 ] L=D \cap (-\infty ,x_1] L=D∩(−∞,x1]
·中段 M = D ∩ [ x 1 , x 2 ] M=D \cap [x_1,x_2] M=D∩[x1,x2]
·右段 R = D ∩ [ x 2 , + ∞ ) R=D \cap [x_2,+\infty) R=D∩[x2,+∞)
在每段上, f , g f,g f,g 的单调性分别为:
·在 L : L: L: f f f 单调不增;同时因为 $x \le x_1 \le x_2 $ ,也有 g g g 在 L L L 上单调不增。
·在 R : R: R: f f f 在 R R R 上单调不减(因为 x ≥ x 1 x \ge x_1 x≥x1) ,且 g g g 在 R R R 上单调不减(因为 x ≥ x 2 x \ge x_2 x≥x2)。
·在中段 M : M: M: f f f 在 M M M 的左半段仍可能单调不增直到 x 1 x_1 x1 ,到 x 1 x_1 x1 后 f f f 变为单调不减; g g g 在 M M M 的右半在 x 2 x_2 x2 前仍单调不增,在 x 2 x_2 x2 后单调不减。总之在 M M M 上,每个函数最多在其极小值点位置改变单调性一次。
断言1
在左段 L L L 因为 f , g f,g f,g 都单调不增,如果存在两个点 u < w ∈ L u<w \in L u<w∈L 使得 f ( u ) ≤ g ( u ) f(u) \le g(u) f(u)≤g(u) 且 f ( w ) ≤ g ( w ) f(w) \le g(w) f(w)≤g(w) ,则对于任意 v v v 满足 u ≤ v ≤ w u \le v \le w u≤v≤w ,有
f ( w ) ≤ f ( v ) ≤ f ( u ) , g ( w ) ≤ g ( v ) ≤ g ( u ) f(w) \le f(v) \le f(u), \qquad g(w) \le g(v) \le g(u) f(w)≤f(v)≤f(u),g(w)≤g(v)≤g(u)
所以从 f ( u ) ≤ g ( u ) f(u) \le g(u) f(u)≤g(u) 与 f ( w ) ≤ g ( w ) f(w) \le g(w) f(w)≤g(w) 可得
f ( v ) ≤ m a x { f ( u ) , f ( w ) } ≤ m i n { g ( u ) , g ( w ) } ≤ g ( v ) , f(v) \le max\{f(u),f(w)\} \le min\{g(u),g(w)\} \le g(v), f(v)≤max{f(u),f(w)}≤min{g(u),g(w)}≤g(v),
因此 f ( v ) ≤ g ( v ) f(v) \le g(v) f(v)≤g(v) ,所以在左段内,若在两个端点都属于 A A A 中间任意点也属于 A A A 。因此 A ∩ L A \cap L A∩L 是区间。
同理如果在左段两个端点均满足 f ( x ) ≥ g ( x ) f(x) \ge g(x) f(x)≥g(x) ,则中间也必然满足 f ( x ) ≥ g ( x ) f(x) \ge g(x) f(x)≥g(x) 。所以在左段 A A A 与 B B B 各自是区间。
断言2
在右段 R R R ,两函数都单调不减,由同理可得 A ∩ R , B ∩ R A \cap R , B \cap R A∩R,B∩R 也都是区间。
断言3
在中段 M = [ x 1 , x 2 ] M=[x_1,x_2] M=[x1,x2] ,每个函数至多在对应极小值点处改变单调性。因为 x 1 ≤ x 2 x_1 \le x_2 x1≤x2 ,中段 M M M 的单调性结构是“先两者都可能非增,接着 f f f 变非减,最后 g g g 变非减”。这限制了 f − g f-g f−g 的符号变化次数: 最多发生一次从负到正再回到负的复杂模式被排除 ,从而 A ∩ M A \cap M A∩M 也是区间。
断言 4
结合前面的断言1-3, A A A 在 L , M , R L,M,R L,M,R 各段内都是区间,且三段相连,因此 A A A 在整个 D D D 上为区间。同理 B B B 也是区间。因为 D = A ∪ B D=A \cup B D=A∪B ,两个区间的并覆盖 D D D 且交集非空,主导函数至多发生一次切换。
从而引理得证。
归纳结论:
则显然有 F k + 1 ( x ) = m a x ( F k ( x ) , f k + 1 ( x ) ) F_{k+1}(x)=max(F_k(x),f_{k+1}(x)) Fk+1(x)=max(Fk(x),fk+1(x)) 仍为单谷函数,所以对任意 n n n 命题成立。
因此我们可以用三分法来解决本题。
C o d i n g Coding Coding
#include <iostream>
#include <cstring>
#include <iomanip>
#include <cmath>
#include <vector>
#include <algorithm>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(x) cout << #x << "=" << x << "\n";
int t;
int n;
const int maxn = 1e4 + 10;
const double eps = 1e-8;
struct func
{
double a, b, c;
double min_val, max_val;
} f[maxn];
inline double calc(double a, double b, double c, double x)
{
return a * x * x + b * x + c;
}
double maxx(double a, double b)
{
return (a > b ? a : b);
}
double calc_point_max(double x)
{
double res = -1e9;
for (int i = 1; i <= n; i++)
res = maxx(res, calc(f[i].a, f[i].b, f[i].c, x));
return res;
}
void solve()
{
cin >> n;
for (int i = 1; i <= n; i++)
cin >> f[i].a >> f[i].b >> f[i].c;
double l = 0, r = 1000;
while (r - l >= eps)
{
double len = r - l;
double m1 = l + len / 3, m2 = r - len / 3;
double res1 = calc_point_max(m1), res2 = calc_point_max(m2);
if (res1 >= res2)
l = m1;
else
r = m2;
}
double ans = calc_point_max((l + r) / 2);
cout << fixed << setprecision(4) << ans << "\n";
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> t;
while (t--)
solve();
return 0;
}
关于时间复杂度:
每次三分都把区间长度缩小为原来的 2 3 \dfrac {2}{3} 32 。
假设初始区间长度为 L 0 = r − l L_0=r-l L0=r−l
迭代 k k k 次后,区间长度变成:
L k = L 0 × ( 2 3 ) k L_k=L_0 \times (\dfrac {2}{3})^k Lk=L0×(32)k
我们希望最终区间长度 ≤ \le ≤ 误差上限 ϵ \epsilon ϵ.
于是有:
L 0 × ( 2 3 ) k ≤ ϵ L_0 \times (\dfrac {2}{3})^k \le \epsilon L0×(32)k≤ϵ
取对数:
k ≥ l o g ( ϵ L 0 ) l o g ( 2 3 ) k \ge \dfrac {log(\frac {\epsilon}{L_0})}{log(\frac {2}{3})} k≥log(32)log(L0ϵ)
通过数学计算可以推导出在绝大多数情况下,我们使用 100 100 100 次迭代就已经绝对安全。
这里由于我是第一次写所以使用了
e
p
s
eps
eps 来估计误差,一开始估计大了直接满江红 ,直接循环
100
100
100 次就好,够快也足够精确。

576

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



