洛谷P3382:(三分/求导+二分)

【模板】三分法

题目描述

如题,给出一个N次函数,保证在范围[l,r]内存在一点x,使得[l,x]上单调增,[x,r]上单调减。试求出x的值。

输入输出格式

输入格式:

第一行一次包含一个正整数N和两个实数lr,含义如题目描述所示。

第二行包含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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值