【模板】三分法
题目描述
如题,给出一个N次函数,保证在范围[l,r]内存在一点x,使得[l,x]上单调增,[x,r]上单调减。试求出x的值。
输入输出格式
输入格式:
第一行一次包含一个正整数N和两个实数l、r,含义如题目描述所示。
第二行包含N+1个实数,从高到低依次表示该N次函数各项的系数。
输出格式:
输出为一行,包含一个实数,即为x的值。四舍五入保留5位小数。
输入输出样例
输入样例#1:
3 -0.9981 0.5
1 -3 -3 1
输出样例#1:
-0.41421
说明
时空限制:50ms,128M
数据规模:
对于100%的数据:7<=N<=13
样例说明:
如图所示,红色段即为该函数f(x)=x^3-3x^2-3x+1在区间[-0.9981,0.5]上的图像。
当x=-0.41421时图像位于最高点,故此时函数在[l,x]上单调增,[x,r]上单调减,故x=-0.41421,输出-0.41421。
题目分析:额,三分求凸性函数,设mL=(L*2+R)/3,mR=(L+R*2)/3,也就是两个三等分点。则我们来比较一下f(mL)和f(mR)。若f(mL)<f(mR)则说明答案一定不在L到mL这一段(YY一下就知道),至于是否在mL-mR还是在mR-R呢,我们尚不清楚。于是此时令L=mL即可,然后重复上述过程。如果f(mL)>f(mR),则答案一定不在R-mR。这样,我们每做一次求f(x)需要O(n)的时间,区间长度变为原来的2/3,时间复杂度为O(n*(R-L)*1000000)。
然而我们还可以发现,因为对于L-ans这段,f(x)单调递增,也就是说其斜率总大于0,而ans-R斜率总小于0,于是我们可以对原函数求导,再用二分。
CODE(三分):
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=19;
int n;
double L,R;
double a[maxn];
double F(double x)
{
double sum=a[0];
double temp=x;
for (int i=1; i<=n; i++)
{
sum+=(a[i]*temp);
temp*=x;
}
return sum;
}
int main()
{
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
scanf("%d%lf%lf",&n,&L,&R);
for (int i=n; i>=0; i--) scanf("%lf",&a[i]);
while (L+0.000001<R)
{
double mL=(2.0*L+R)/3.0;
double mR=(L+2.0*R)/3.0;
if ( F(mL)<F(mR) ) L=mL;
else R=mR;
}
printf("%.5lf",L);
return 0;
}
CODE(求导+二分):
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=13;
int n;
double a[maxn];
double L,R;
double F(double x)
{
double sum=a[0];
double temp=x;
for (int i=1; i<n; i++)
{
sum+=(a[i]*temp);
temp*=x;
}
return sum;
}
int main()
{
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
scanf("%d%lf%lf",&n,&L,&R);
for (int i=n-1; i>=0; i--)
{
scanf("%lf",&a[i]);
a[i]=a[i]*(double)(i+1);
}
while (L+0.000001<R)
{
double mid=(L+R)/2.0;
if ( F(mid)>0.0 ) L=mid;
else R=mid;
}
printf("%.5lf\n",L);
return 0;
}