【题目描述】
明明做作业的时候遇到了n个二次函数Si(x)= ax2 + bx + c,他突发奇想设计了一个新的函数F(x) = max(Si(x)), i = 1...n.
明明现在想求这个函数在[0,1000]的最小值,要求精确到小数点后四位四舍五入。
【输入数据】
输入包含T 组数据 (T < 10) ,每组第一行一个整数 n(n ≤ 10000) ,之后n行,每行3个整数a (0 ≤ a ≤ 100), b (|b| ≤ 5000), c (|c| ≤ 5000) ,用来表示每个二次函数的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
好吧 一开始想的是 F(x)肯定在某两个函数的交点处改变单调性
然后暴力枚举交点 O(n)验证是否为所有函数图象中最上面的的那个(在F(x)上)
然后事实证明可以过50分
但是自己没写啊啊啊啊啊啊啊啊啊
用随机写的 撒点 随机移动
结果一个点也没过!!!
下午重新改了改 突然发现
数据范围中 a 没加绝对值!!!
这说明什么呢?
说明F(x)是一个向下凸的单峰函数!!!
然后就可以爬山算法啦
嫌爬山麻烦 就写了个三分。。。
话说对精度要求太高了 要1e-9才可过
估计是有的函数太“陡”了吧
丧心病狂!!!还好不是noip
#include<iostream>
#include<cstdio>
#include<cstring>
#include<iomanip>
using namespace std;
double l,r,mid,midmid;
double ymid,ymidmid;
struct self{double x,y,w;}s[10011];
int m,a,b,c,d;
int casenum;
double f(int i,double x)
{
return s[i].x*x*x+s[i].y*x+s[i].w;
}
double finduper(double i)
{
double ret=-999999999;
for(int a=1;a<=m;a++)
ret=max(ret,f(a,i));
return ret;
}
int main()
{
freopen("curves.in","r",stdin);
freopen("curves.out","w",stdout);
scanf("%d",&casenum);
while(casenum)
{
casenum--;
scanf("%d",&m);
for(a=1;a<=m;a++)scanf("%lf%lf%lf",&s[a].x,&s[a].y,&s[a].w);
l=0;r=1000;
while(l<r-1e-9)
{
mid=(l+r)/2;
midmid=(mid+r)/2;
ymid=finduper(mid);
ymidmid=finduper(midmid);
if(ymid<ymidmid)r=midmid;
else l=mid;
}
cout<<fixed<<setprecision(4)<<ymid<<endl;
}
return 0;
}
另外附上二分的题解
本题的关键是二分答案。由于输出只要求精确到4位,我们可以二分结果。设结果为ans,对于二分得到的任何一个值mid,我们可以计算出每条二次函数值低于它的区间。如果mid < ans,那么刚才所说的那些区间的交集为空,如果mid > ans 那么刚才所说的那些区间的交集就不为空了。
所以现在我们只需要进行二分搜索答案ans,每次通过把数带入二次函数计算出每个二次函数所对应的区间,然后判断这些区间的交集是否为空即可。判断区间交集是否为空可以直接判断他们最左的右端点是否在最右的左端点右边即可。每次二分判断的时间复杂度为O(n),整个算法的时间复杂度为O(pn), p为二分的次数。