如果一个函数f 在区间[l,r]上只有一个波峰,求其极值,可用三分。
mid=(l+r)/2; midmid=(mid+r)/2;
如果 val(mid) 大于val(midmid) 则 r=midmid,反之l=mid;(求最大值)
模板
<span style="font-family:FangSong_GB2312;font-size:14px;"><strong><span style="font-family:FangSong_GB2312;font-size:14px;">const double esp=1e-5;
while (r-l>=esp){
double mid=(r+l)/2;
double midmid=(mid+r)/2;
if (val[mid]>val[midmid])
r=midmid;
else
l=mid;
}
</span></strong></span>
相关题目:
hdu 4454 三分两次圆上的点,两次起始点不同
<span style="font-family:FangSong_GB2312;font-size:14px;"><strong><span style="font-family:FangSong_GB2312;font-size:14px;">#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const double eps=1e-10;
const double PI=acos(-1);
struct point{
double x,y,z;
point(double a=0,double b=0){
x=a,y=b;
}
}begin_point,z_lu,z_ru,z_ld,z_rd,center;
double r;
double dist(point a,point b){
double x=a.x-b.x,y=a.y-b.y;
return sqrt(x*x+y*y);
}
double xmult(point p1,point p2,point p0){
return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y);
}
point intersection(point u1,point u2,point v1,point v2){
point ret=u1;
double t=((u1.x-v1.x)*(v1.y-v2.y)-(u1.y-v1.y)*(v1.x-v2.x))
/((u1.x-u2.x)*(v1.y-v2.y)-(u1.y-u2.y)*(v1.x-v2.x));
ret.x+=(u2.x-u1.x)*t;
ret.y+=(u2.y-u1.y)*t;
return ret;
}
point p_to_s(point p,point l1,point l2){
point t=p;
t.x+=l1.y-l2.y,t.y+=l2.x-l1.x;
if (xmult(l1,t,p)*xmult(l2,t,p)>eps)
return dist(p,l1)<dist(p,l2)?l1:l2;
return intersection(p,t,l1,l2);
}
double val(double theta){
double x,y,ans;
y=sin(theta)*r+center.y;
x=cos(theta)*r+center.x;
//printf("%lf %lf\n",x,y);
point a;a.x=x;a.y=y;
ans=dist(a,p_to_s(a,z_lu,z_ld));
ans=min(dist(a,p_to_s(a,z_lu,z_ru)),ans);
ans=min(dist(a,p_to_s(a,z_ru,z_rd)),ans);
ans=min(dist(a,p_to_s(a,z_ld,z_rd)),ans);
ans+=dist(a,begin_point);
return ans;
}
int main (){
//freopen("test.in","r",stdin);
double a,b,c,d;
while (~scanf("%lf%lf",&a,&b)){
if (a==0&&b==0) break;
begin_point.x=a;
begin_point.y=b;
scanf("%lf%lf",&a,&b);
center.x=a;
center.y=b;
scanf("%lf",&r);
scanf("%lf%lf%lf%lf",&a,&b,&c,&d);
z_lu.x=min(a,c);z_lu.y=max(b,d);
z_ru.x=max(a,c);z_ru.y=max(b,d);
z_ld.x=min(a,c);z_ld.y=min(b,d);
z_rd.x=max(a,c);z_rd.y=min(b,d);
double l=0,r=PI*2;
while (r-l>=eps){
double mid=(r+l)/2;
double midmid=(mid+r)/2;
if (val(mid)<val(midmid)) r=midmid;
else l=mid;
}
double ans=val(l);
l=PI,r=PI*2+l;
while (r-l>=eps){
double mid=(r+l)/2;
double midmid=(mid+r)/2;
if (val(mid)<val(midmid)) r=midmid;
else l=mid;
}
ans=min(val(l),ans);
printf("%.2lf\n",ans);
}
return 0;
}
</span></strong></span>
poj 3301
将坐标轴旋转a度(clock-wise)
坐标变化公式
X’=X*cos(a)-Y*sin(a);
Y’=Y*cos(a)+X*sin(a);
三分旋转角度
<span style="font-family:FangSong_GB2312;font-size:14px;"><strong>#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const double esp=1e-10;
const double PI=acos(-1);
struct node {
double x,y;
}point[50];
int cnt;
double val(double a){
double xmin=1000000000,xmax=0,ymin=1000000000,ymax=0;
for (int i=0;i<cnt;i++){
double x=point[i].x*cos(a)-point[i].y*sin(a);
double y=point[i].y*cos(a)+point[i].x*sin(a);
xmin=min(x,xmin);
xmax=max(x,xmax);
ymin=min(y,ymin);
ymax=max(y,ymax);
}
double ans=max(xmax-xmin,ymax-ymin);
return ans*ans;
}
int main (){
//freopen("test.in","r",stdin);
int T;scanf("%d",&T);
while (T--){
scanf("%d",&cnt);
for (int i=0;i<cnt;i++){
scanf("%lf%lf",&point[i].x,&point[i].y);
}
double l=0,r=PI*2;
while (r-l>=esp){
double mid=(r+l)/2;
double midmid=(mid+r)/2;
if (val(mid)<val(midmid))
r=midmid;
else
l=mid;
}
double ans=val(l);
l=PI,r=PI*2+l;
while (r-l>=esp){
double mid=(r+l)/2;
double midmid=(mid+r)/2;
if (val(mid)<val(midmid))
r=midmid;
else
l=mid;
}
ans=min(ans,val(l));
printf("%.2f\n",ans);
}
return 0;
}
</strong></span>
hdu 3400
两次三分(嵌套使用)
第一次,三分a,b上的点
第二次,在确定a,b上的点的时候,三分c,d上的点求最小值
<span style="font-family:FangSong_GB2312;font-size:14px;"><strong>#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
const double eps=1e-8;
struct point{
double x,y,z;
point(double a=0,double b=0){
x=a,y=b;
}
}a,b,c,d;
double P,Q,R;
double dist(point a,point b){
double x=a.x-b.x,y=a.y-b.y;
return sqrt(x*x+y*y);
}
point MID(point a,point b){
point c;
c.x=(a.x+b.x)/2;
c.y=(a.y+b.y)/2;
return c;
}
double val0(point e,point f){
return (dist(a,e)/P+dist(e,f)/R+dist(f,d)/Q);
}
double val(point e){
point l=c,r=d;
double ans;
while (dist(r,l)>=eps){
point mid=MID(r,l);
point midmid=MID(mid,r);
if (val0(e,mid)<val0(e,midmid))
r=midmid;
else
l=mid;
ans=val0(e,mid);
}
return ans;
}
int main (){
//freopen("test.in","r",stdin);
int T;scanf("%d",&T);
while (T--){
scanf("%lf%lf",&a.x,&a.y);
scanf("%lf%lf",&b.x,&b.y);
scanf("%lf%lf",&c.x,&c.y);
scanf("%lf%lf",&d.x,&d.y);
scanf("%lf%lf%lf",&P,&Q,&R);
point l=a,r=b;
while (dist(r,l)>=eps){
point mid=MID(r,l);
point midmid=MID(mid,r);
if (val(mid)<val(midmid))
r=midmid;
else
l=mid;
}
printf("%.2lf\n",val(l));
}
return 0;
}
</strong></span>
ZOJ 3203
三分距离灯光的水平距离
<span style="font-family:FangSong_GB2312;font-size:14px;"><strong>#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
double H,D,h;
double val(double d){
double ans=H*d/(H-h);
if (ans>D) return (H-(H-h)*D/d+D-d);
return ans-d;
}
int main (){
int T;scanf("%d",&T);
while (T--){
scanf("%lf%lf%lf",&H,&h,&D);
const double esp=1e-9;
double l=0,r=D;
while (r-l>=esp){
double mid=(r+l)/2;
double midmid=(mid+r)/2;
if (val(mid)>val(midmid))
r=midmid;
else
l=mid;
}
printf("%.3lf\n",val(l));
}
return 0;
}
</strong></span>
hdu 2438
三分车辆转弯时的旋转角度
<span style="font-family:FangSong_GB2312;font-size:14px;"><strong>#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
double x,y,l,w;
double val(double tmp){
return (-x+l*sin(tmp)+w/cos(tmp))/tan(tmp);
}
int main (){
while (~scanf("%lf%lf%lf%lf",&x,&y,&l,&w)){
const double esp=1e-10;
double l=0,r=3.1415926/2;
if (w>x||w>y) {
printf("no\n");
continue;
}
while (r-l>=esp){
double mid=(r+l)/2;
double midmid=(mid+r)/2;
if (val(mid)>val(midmid))
r=midmid;
else
l=mid;
}
if (val(l)>y)printf("no\n");
else printf("yes\n");
}
return 0;
}
</strong></span>