蒟蒻不会凸包,于是用模拟退火过了这道题。
如果你还不会模拟退火,可以看看我之前写的学习笔记。
题意
来自辰星凌的题解:
给出 $n$ 个点 $(x_i,y_i)$,设 $f_i(a,b)=x_i+y_i+\frac{bx_i}{a}+\frac{ay_i}{b}$,求出一组 $(a,b)$,使得 $\max\{f_{i\in[1,n]}(a,b)\}$ 最小,输出这个最小值。
做法
正常模拟退火流程,每次随机生成两个数 $(a,b)$,暴力遍历一遍所有点计算出当前的答案,与上一次的答案比较。
蒟蒻一开始只是想骗点分,没想到拿了 $80$ 分,于是就进入了痛苦的调参过程。(注:没有在洛谷提交,洛谷数据不全。)
最初提交时总是第 $7$ 个点错,后来有一次分比较低,但是第 $7$ 个点对了,我比较了一下认为是初始温度的问题,初始温度低会导致生成出来的数的范围小,而那个点最优情况下的 $(a,b)$ 大于这个范围,所以就一直错。
于是稍微修改一下参数就 $A$ 了,最后一共提交了 $172$ 次…
不开 $O2$ 会 $T$,时间复杂度 $O(\text{玄学})$。
代码
#include <bits/stdc++.h>
#define rd t*(rand()*2-RAND_MAX)
#define max(x,y) (x<y?y:x) //这里是个小优化,会快一点
using namespace std;
typedef long long ll;
const double eps=1e-6,down=0.89;
const int mn=1e6+7;
int n;
double x[mn],y[mn];
double sol(double a,double b)
{
double rs=0;
for(int i=1;i<=n;++i)
{
double tmp=x[i]+y[i]+x[i]/a*b+y[i]/b*a;
rs=max(rs,tmp);
}
return rs;
}
int main()
{
srand(154051);
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%lf%lf",&x[i],&y[i]);
ll a=1,b=1;
double ans,minn;
minn=ans=sol(1,1);
for(double t=1100000;t>eps;t*=down)
{
ll aa=a+rd,bb=b+rd;
if(aa==0) aa++;
if(bb==0) bb++;
if(aa<0) aa=-aa;
if(bb<0) bb=-bb;
double rs=sol(aa,bb);
if(minn>rs) minn=rs;
if(rs<ans||exp((ans-rs)/t)*RAND_MAX>rand())
{
ans=rs;
a=aa;b=bb;
}
}
printf("%.4f",minn);
return 0;
}