原题链接
P3382
题目类型:
普
及
/
提
高
−
{\color{yellow} 普及/提高-}
普及/提高−
AC记录:Accepted
题目大意
给你一个数列,你需要下面两种操作:
如题,给出一个 n n n 次函数,保证在范围 [ l , r ] [l,r] [l,r] 内存在一点 x x x,使得 [ l , r ] [l,r] [l,r]上单调增, [ x , r ] [x,r] [x,r]上单调减。试求出 x x x的值。
输入格式
第一行包含一个正整数
n
n
n和两个实数
l
,
r
l,r
l,r,含义如题目描述所示。
第二行包含
n
+
1
n+1
n+1个实数,从高到低依次表示该
N
N
N次函数各项的系数。
输出格式
输出为一行,包含一个实数,即为 x x x的值。若你的答案和标准答案的绝对误差不超过 1 0 − 5 10^{-5} 10−5则算正确。
S a m p l e \mathbf{Sample} Sample I n p u t \mathbf{Input} Input
3 -0.9981 0.5
1 -3 -3 1
S a m p l e \mathbf{Sample} Sample O u t p u t \mathbf{Output} Output
-0.41421
H
i
n
t
&
E
x
p
l
a
i
n
\mathbf{Hint\&Explain}
Hint&Explain
数据范围
对于 100 % 100\% 100%的数据, 6 ≤ n ≤ 13 6\le n\le 13 6≤n≤13,函数系数均在 [ − 100 , 100 ] [-100,100] [−100,100]内且至多 15 15 15位小数, ∣ l ∣ , ∣ r ∣ ≤ 10 |l|,|r|\le 10 ∣l∣,∣r∣≤10且至多 15 15 15位小数。 l ≤ r l\le r l≤r。
解题思路
这是一道三分法的模板题。
三分法和二分法非常像,就是把原来的用一个点分成两段改成了用两个点分成三段。设第一个点为
x
x
x,第二个点为
y
y
y,题目给我们的
n
n
n次函数为
f
(
x
)
f(x)
f(x),以下同。
三分的策略为:
1. 如 果 f ( x ) < f ( y ) , 则 舍 去 左 半 段 \color{red}1.如果f(x)<f(y),则舍去左半段 1.如果f(x)<f(y),则舍去左半段
2. 如 果 f ( x ) > f ( y ) , 则 舍 去 右 半 段 \color{orange}2.如果f(x)>f(y),则舍去右半段 2.如果f(x)>f(y),则舍去右半段
3. 重 复 1 , 2 直 到 ∣ l − r ∣ ≤ 1 0 − 5 \color{green}3.重复1,2直到|l-r|\le 10^{-5} 3.重复1,2直到∣l−r∣≤10−5
按着这个做就可以了。
小技巧
1.通常人们会把区间分成三等分,其实只要把 x x x设成 m i d − 1 0 − 5 mid-10^{-5} mid−10−5, y y y设成 m i d + 1 0 − 5 mid+10^{-5} mid+10−5就可以了。这样就可以在每一次舍弃区间时都舍掉将近 1 2 \frac{1}{2} 21的长度,提高效率。
2.在计算函数的时候用秦九韶算法,提高效率。
最后,祝大家早日
上代码
#include<iostream>
#include<cmath>
using namespace std;
double eps=1e-7;
int n;
double l,r;
double num[23];
double f(double x)
{
double sum=0;
for(int i=1; i<=n+1; i++)
sum=sum*x+num[i];
return sum;
}
int main()
{
cin>>n;
cin>>l>>r;
for(int i=1; i<=n+1; i++)
cin>>num[i];
while(fabs(l-r)>eps)
{
double mid=(l+r)/2.0;
if(f(mid-eps)<f(mid+eps))
l=mid;
else
r=mid;
}
cout<<l<<endl;
return 0;
}
完美切题 ∼ \sim ∼