#LOJ10013 曲线(三分)
题目描述
明明做作业的时候遇到了 n 个二次函数 Si(x)=ax^2 + bx + c,他突发奇想设计了一个新的函数 F(x)=max{Si(x)},i=1…n。
明明现在想求这个函数在 [0,1000] 的最小值,要求精确到小数点后四位,四舍五入。
输入格式
输入包含 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
数据范围与提示
对于 50% 的数据,1≤n≤100;
对于 100% 的数据,1≤T≤10, 1≤n≤10^5, 0≤a≤100, 0≤∣b∣≤5000, 0≤∣c∣≤5000。
三分法适用于求解凸性函数的极值问题,二次函数就是一个典型的单峰函数。
设当前求解的区间为[l,r],令,
,接着计算这两个点的函数值
,
之后将两点中函数值更优的那个点称为“好点”(若计算最小值,则f更小的那个点就为好点),而函数值较差的那个点称为“坏点”。
可以利用反证法证明:最优点和好点在坏点的同侧。
如图,f(m2)>f(m1),最大值为最优点,因此m1是坏点,m2是好点,最优点与m2会位于m1同侧,求解区间由[l,r]变为[m1,r]。由此可以不停缩小求解区间,直至得出近似解。
如果函数不严格单调,三分法将不适用。
由于a大于等于0,函数S是开口向上的二次函数(当a=0时是一次函数),即S为先单调递减,后单调递增的下凸函数,或者是一次单调函数,F(x)=max{Si(x)}也满足单调性。因此可用三分法求解[0,1000]内的最小值。
AC代码:
////****博客:https://blog.****.net/qq_40889820
#include<iostream>
#include<sstream>
#include<algorithm>
#include<string>
#include<cstring>
#include<iomanip>
#include<vector>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
#include<map>
#define mem(a,b) memset(a,b,sizeof(a))
#define e 2.71828182
#define Pi 3.141592654
#define INF 1<<30
using namespace std;
struct node
{
double a,b,c;
}quad[10010];
int n;
double fun(double x)//F(x)
{
double ans=-INF;
for(int i=1;i<=n;i++)
ans=max(ans,quad[i].a*x*x+quad[i].b*x+quad[i].c);
return ans;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(NULL);cout.tie(NULL);
int T;
cin>>T;
while(T--)
{
cin>>n;
for(int i=1;i<=n;i++) cin>>quad[i].a>>quad[i].b>>quad[i].c;
double l=0.0,r=1000.0;//[0,1000]
while(r-l>1e-11)
{
double mid1=l+(r-l)/3,mid2=r-(r-l)/3;
if(fun(mid1)<fun(mid2))//mid1是好点
r=mid2;
else l=mid1;
}
cout<<setiosflags(ios::fixed)<<setprecision(4)<<fun(l)<<endl;
}
}