学习三分 (概念 + 模板 + 例题:曲线)

这好像是我第一次尝试写一个新知识入门
而不是习题解

在这里插入图片描述

三分概念

我们都知道,二分是在一个单调函数(即一次函数)上通过每次查找折半的方式,对答案进行搜索查找。那么,三分就是在一个单峰函数(二次函数,抛物线)上,不断地将答案分为三份,
通过两者的比较来求取这个峰值(极值)的答案。
对于所要求解的范围[L,R],计算出两个点M1和M2将整个范围分为三段,
M1在从左数1/3处,M2在从左数2/3处。每次计算时,计算M1处和M2处的值,
在这两个点中,更接近我们想要的答案的那个值称为好点,另一个称为坏点,
并更新靠近坏点那一侧的边界值。最后直到搜索到我们的答案为止。

也就是如果我们要找最大值,且M1>M2就三分l~M2,即把右端点r变为M2

为什么是靠近坏点呢?以求最大值为例(凸峰函数,a<0)解释
因为我们是三分答案,有可能出现M1,M2都在我们答案点的左边或者右边,

假设都在右边,M1的值大于M2的值,如果我们靠近好点,把l变成了M1,就彻底离开了答案的怀抱!!
反之,靠近坏点,更改r,在新的[l,r]区间内仍是包含着答案点的!

换言之,我们找到了两个点,M1优于M2,那么[M2,r]也一定是不优于M1的,就可以大胆舍去
但我们不能保证[l,M1]全都不优于M1,无法割舍的爱

模板

三分是与二分相似的,count就是我们对答案正确性的判断

int count ( int x ) {
......//进行答案处理
}
void sanfen ( double l, double r ) {
	if ( ( r - l ) < eps ) {
		id = l;
		return;
	}
	double mid1 = l + ( r - l ) / 3.0;
	double mid2 = r - ( r - l ) / 3.0;
	if ( count ( mid1 ) < count ( mid2 ) )
		sanfen ( l, mid2 );
	else
		sanfen ( mid1, r );
}

例题:曲线

题目

给定n个二次函数f1(x),f2(x),…,fn(x)(均形如ax^2+bx+c),设F(x)=max{f1(x),f2(x),…,fn(x)},求F(x)在区间[0,1000]上的最小值。
在这里插入图片描述

输入格式
输入第一行为正整数T,表示有T 组数据。

每组数据第一行一个正整数n,接着n行,每行3个整数a,b,c ,用来表示每个二次函数的3个系数,
注意二次函数有可能退化成一次。

输出格式
每组数据输出一行,表示F(x)的在区间[0,1000]上的最小值。答案精确到小数点后四位,四舍五入。

输入输出样例
输入
2
1
2 0 0
2
2 0 0
2 -4 2
输出
0.0000
0.5000
说明/提示
【数据范围】
T < 10, n ≤ 10000,0 ≤ a ≤ 100,|b| ≤ 5000, |c| ≤ 5000 前50%数据n ≤ 100

题解

说了这道题是例题,那么肯定是三分啦!but。。。
我竟然和仙女同学们一起讨论了为什么是三分
在这里插入图片描述
我们以题目要求来分析,用最简单的两个函数为例,如图:
在这里插入图片描述
每次都要取最大值,也就意味着当至少两个函数遇到相交点,
如果第一个函数开始递增,那么就必定函数逐步递减有一段会代替它,接上它
就根本不会出现大波浪卷发的形状,不能理解也没关系,好好想想就行了!这不重要

这道题就是个模板,所以没有什么思维可讲,唯一有点臭不要脸的就是,精度!!!
eps不能卡着四位小数开,
因为题目要的是y也就是函数结果的精度四位,而我们三分的是x,精度要更高开个1e-9就能卡过了
我被卡哭了,本仙女也不会告诉你我刚开始连题目都理解错了~
在这里插入图片描述

代码实现

#include <cstdio>
#include <iostream>
using namespace std;
#define MAXN 100005
#define eps 1e-9
#define L 0
#define R 1000
int n, t;
double a[MAXN], b[MAXN], c[MAXN];
double id;

double count ( double x ) {
	double val = -2147483647;
	for ( int i = 1;i <= n;i ++ )
		val = max ( a[i] * x * x + b[i] * x + c[i], val );
	return val;
}

void sanfen ( double l, double r ) {
	if ( ( r - l ) < eps ) {
		id = l;
		return;
	}
	double mid1 = l + ( r - l ) / 3.0;
	double mid2 = r - ( r - l ) / 3.0;
	if ( count ( mid1 ) < count ( mid2 ) )
		sanfen ( l, mid2 );
	else
		sanfen ( mid1, r );
}

int main() {
	scanf ( "%d", &t );
	while ( t -- ) {
		scanf ( "%d", &n );
		for ( int i = 1;i <= n;i ++ )
			scanf ( "%lf %lf %lf", &a[i], &b[i], &c[i] );
		sanfen ( L, R );
		printf ( "%.4lf\n", count ( id ) );
	}
	return 0;
} 

好了,三分就这样啦,感觉。。。
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值