三分是指之前接触过的一个算法,但是没有用来写过题。
最近
a
t
a
b
c
130
at abc130
atabc130的
F
F
F题出到了,结果压根没想到要用三分。
题意:对于
n
n
n个点,要求使得这
n
n
n个点
x
x
x轴上的最大值-最小值和
y
y
y轴上的最大值-最小值乘积最小。通过样例可以发现时间显然可以是小数。那么就是函数求极值的问题了。
事实上,我并不会证明此题为什么是单峰函数。但是用三分可以忽略这些,直接解决。(这样求极值肯定是用三分。
至于三分。
我们考虑这样一个单峰函数(三分只适用于单峰函数。
此时区间为
[
l
,
r
]
[l,r]
[l,r],两个三等分点
m
1
m1
m1,
m
2
m2
m2,如果
f
(
m
1
)
>
f
(
m
2
)
f(m1)>f(m2)
f(m1)>f(m2),那么极值点所在区间应该是
[
m
1
,
r
]
[m1,r]
[m1,r]。反之,所在区间为
[
L
,
m
2
]
[L,m2]
[L,m2]
求极小值的话全都反过来。这个动手画画图就可以判断了,没有必要特意记。
唯一需要记住的是:
三分的写法和二分的写法有所不同。
- 它是以循环 300 300 300次或者其他次数来得到的,否则容易 T L E TLE TLE,我比较喜欢 300 300 300。
- 处理答案的时候,直接取每次计算的最小值(指求极小值。
- 如果是求极值点,左右 l , r l,r l,r皆可。(暂未实践。
次数主要决定于原区间长度
x
(
2
3
)
k
≈
0
x(\frac{2}{3})^{k}≈0
x(32)k≈0,
k
k
k是多少次数就应该大致是多少,最好多一些。
这题就可以解决了,求答案直接计算即可,存储一下每个点的方向。
#include<bits/stdc++.h>
#define FOR(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
int n;
struct node{
double x,y;
char dir[4];
}A[100050];
double cal(double x){
double ml=1e10,mr=-1e10,mu=-1e10,md=1e10;
FOR(i,1,n){
pair<double,double>tmp;
tmp.first=A[i].x,tmp.second=A[i].y;
switch(A[i].dir[0]){
case 'U':tmp.second+=x;break;
case 'D':tmp.second-=x;break;
case 'L':tmp.first-=x;break;
case 'R':tmp.first+=x;break;
}
ml=min(ml,tmp.first);
mr=max(mr,tmp.first);
mu=max(mu,tmp.second);
md=min(md,tmp.second);
}
//cout<<x<<" "<<(mr-ml)*(mu-md)<<endl;
return (mr-ml)*(mu-md);
}
int main(){
cin>>n;
FOR(i,1,n){
scanf("%lf%lf%s",&A[i].x,&A[i].y,A[i].dir);
}
double l=0,r=1e12,ans=1e18;
FOR(k,0,300){
double m1=(l+(r-l)/3),m2=(l+(r-l)*2/3);
double tmp1=cal(m1),tmp2=cal(m2);
if(tmp1>tmp2)l=m1;
else r=m2;
ans=min(ans,tmp1);
ans=min(ans,tmp2);
}
printf("%.10f",ans);
}