https://www.luogu.org/problemnew/show/P3382
题目描述
如题,给出一个N次函数,保证在范围[l,r]内存在一点x,使得[l,x]上单调增,[x,r]上单调减。试求出x的值。

输入输出格式
输入格式:
第一行一次包含一个正整数N和两个实数l、r,含义如题目描述所示。

第二行包含N+1个实数,从高到低依次表示该N次函数各项的系数。

输出格式:
输出为一行,包含一个实数,即为x的值。四舍五入保留5位小数。

原理简单,且适用于所有在定义域中单调性只改变一次的函数,
就是比较定义域中的两个三等分点的映射值,若左边的三等分点比较大,则将右边界右移,对于左边界同理,最终不断逼近得到单峰的位置。
三等分点怎么求,左边界右边三分之一的区间长度和右边界左边三分之一的区间长度。

三分法

#include <bits/stdc++.h>
#define fastio ios::sync_with_stdio(false);cin.tie(0)
using namespace std;
#define int long long
typedef long long ll;
typedef pair<int, int> P;

const int maxn = 100000 + 5 ;
const int INF = 0x3f3f3f3f ;
const int mod = 9901 ;
const double eps = 1e-6;

int n;
double a[25];

double fun(double x) {
	double res = 0;
	for(int i = 0; i <= n; i++)
		res = res * x + a[i];
	return res;
}

signed main() {
	// fastio;
	double l, r, midl, midr;
	cin >> n >> l >> r;

	for(int i = 0; i <= n; i++)
		cin >> a[i];

	while(r - l > eps) {
		double d = (r - l) / 3.0;
		midl = (l + d), midr = (r - d);

		if(fun(midl) > fun(midr))
			r = midr;
		else
			l = midl;
	}
	printf("%.5lf\n", l);
	return 0;
}

求导二分

当然 由于这道题是多项式给的好 所以 幂函数求导 然后二分 求导是0的点必然对于这个单峰函数来说是极点

导数的函数对应的值表示每一点的切线斜率,所以当该点的导数值大于0则单调增,小于零则单调减,所以只需判断是否大于零即可。
缺点,如果换一道题不是多项式函数的话就不能用这个求单峰了。

#include <bits/stdc++.h>
#define fastio ios::sync_with_stdio(false);cin.tie(0)
using namespace std;
#define int long long
typedef long long ll;
typedef pair<int, int> P;

const int maxn = 100000 + 5 ;
const int INF = 0x3f3f3f3f ;
const int mod = 9901 ;
const double eps = 1e-6;

int n;
double a[25];

double fun(double x) {
	double res = 0;
	for(int i = 0; i < n; i++) // 常数项 消失 求导完 
		res = res * x + a[i];
	return res;
}

signed main() {
	// fastio;
	double l, r, midl, midr;
	cin >> n >> l >> r;

	for(int i = 0; i <= n; i++)
		cin >> a[i], a[i] *= (n-i);

	while(r - l > eps) {
		double mid = (r + l) / 2.0;

		if(fun(mid) > 0)
			l = mid;
		else
			r = mid;
	}
	printf("%.5lf\n", l);
	return 0;
}