这好像是我第一次尝试写一个新知识入门
而不是习题解
文章目录
- 三分概念
- 模板
- 例题:曲线
- 题目
- 题解
- 代码实现
三分概念
我们都知道,二分是在一个单调函数(即一次函数)上通过每次查找折半的方式,对答案进行搜索查找。那么,三分就是在一个单峰函数(二次函数,抛物线)上,不断地将答案分为三份,
通过两者的比较来求取这个峰值(极值)的答案。
对于所要求解的范围[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 );elsesanfen ( 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 );elsesanfen ( 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;
}
好了,三分就这样啦,感觉。。。