信息学奥赛一本通 二分查找
【题目描述】
明明做作业的时候遇到了nn个二次函数Si(x)=ax2+bx+c,他突发奇想设计了一个新的函数F(x)=max(Si(x)), i=1,2...n。
明明现在想求这个函数在[0,1000]的最小值,要求精确到小数点后四位四舍五入。
【输入】
输入包含TT 组数据 (T<10) ,每组第一行一个整数 n(n≤10000)) ,之后n行,每行3个整数a(0≤a≤100),b(|b|≤5000),c(|c|≤5000),用来表示每个二次函数的3个系数,注意二次函数有可能退化成一次。
【输出】
每组数据一个输出,表示新函数F(x)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。
【分析】
如果a>0,则函数图像是开口向上的抛物线,有最低值。如果a==0,则函数图像是一条直线,也有最低值。所以选择x由两端0,1000向中间逼近,x1,x2分别取值,最终x1==x2时,得到最低值。
l_mid = left + (right - left)/3;//左边取值 1/3
r_mid = right - (right - left)/3;//右边取值 1/3
在进行查找时,根据函数值结果来更新left、right。
y1 = calc(l_mid);//左边函数值
y2 = calc(r_mid);//右边函数值
/*
y1 < y2,说明最低点在左边,更新right
y1 == y2,说明最低点在l_mid与r_mid中间,更新left或right
y1 > y2,说明最低点在右边,更新left
*/
if(y1 <= y2){
right = r_mid;
}else{
left = l_mid;
}
【完整代码】
#include <bits/stdc++.h>
using namespace std;
struct Node{
int a,b,c;
}s[10005];
int n;
double calc(double x){
double ans = -1e9;
//n行参数,选函数值最大的
for(int i = 0; i < n; i++){
ans = max(ans, s[i].a*x*x + s[i].b*x + s[i].c);
}
return ans;
}
int main(int argc, char *argv[]) {
int t;
cin >> t;
for(int i = 0; i < t; i++){//t组
cin >> n;
for(int j = 0; j < n; j++){
cin >> s[j].a >> s[j].b >> s[j].c;
}
double left = 0, right = 1000;
double l_mid, r_mid, y1, y2;
//二分查找,方程的抛物线上有两个x,由两边0~1000分别向中间进行计算
while(fabs(right - left) > 1e-9){
l_mid = left + (right - left)/3;//左边取值 1/3
r_mid = right - (right - left)/3;//右边取值 1/3
y1 = calc(l_mid);//左边函数值
y2 = calc(r_mid);//右边函数值
/*
y1 < y2,说明最低点在左边,更新right
y1 == y2,说明最低点在l_mid与r_mid中间,更新left或right
y1 > y2,说明最低点在右边,更新left
*/
if(y1 <= y2){
right = r_mid;
}else{
left = l_mid;
}
}
//输出,最低点时y1==y2,输出y2也可以
cout << fixed <<setprecision(4) << y1 << endl;
}
return 0;
}