三分法小结
来源:互联网 发布:万国数据代管服务器吗 编辑:程序博客网 时间:2024/06/06 02:51
昨天晚上快要回宿舍的时候偶然发现一道看起来简单的计算几何题,搜了一下题解,遇见一个新的方法,三分法,以前只是听过二分法,于是今天刷了几道三分法的题目。
(上图来自http://hi.baidu.com/czyuan_acm/item/81b21d1910ea729c99ce33db)
二分法主要用来搜索单调函数的某个值,而三分法主要用于凸(凹)函数的极值。以凸函数为例,如果mid=(left+right)/2,midmid=(mid+right)/2,fun(mid)>=fun(midmid),则right=midmid;
例1:hdu(4454) stealing a cake
给你一个点,一个圆,一个矩形,问从这个点到圆,然后到矩形的最短距离。
就是从这一题注意三分法的。注意到在(0,PI),(PI,2*PI)内距离先减小后增加(sad,不会证明T^T……),分别三分角度即可
#include <cmath>#include <cstdio>#include <cstring>#include <iostream>using namespace std;const double eps=1e-8;const double pi=acos(-1);struct Point{ double x,y; Point(double x=0,double y=0) :x(x),y(y) { }};Point operator-(const Point &lhs,const Point &rhs){ return Point(lhs.x-rhs.x,lhs.y-rhs.y);}int dcmp(double a){ if(fabs(a)<=eps) return 0; return a<0?-1:1;}bool operator==(const Point &a,const Point &b){ return dcmp(a.x-b.x)==0&&dcmp(a.y-b.y)==0;}double cross(const Point &a,const Point &b){ return a.x*b.y-a.y*b.x;}double dot(const Point &a,const Point &b){ return a.x*b.x+a.y*b.y;}double length(const Point &a){ return sqrt(dot(a,a));}double dist2line(const Point &p,const Point &a,const Point &b){ if(a==b) return length(p-a); Point v1=b-a,v2=p-a,v3=p-b; if(dcmp(dot(v1,v2))<0) return length(v2); if(dcmp(dot(v1,v3))>0) return length(v3); return fabs(cross(v1,v2))/length(v1);}double dist2rec(const Point &p,Point *rec){ double ans=dist2line(p,rec[0],rec[1]); ans=min(dist2line(p,rec[1],rec[2]),ans); ans=min(dist2line(p,rec[2],rec[3]),ans); ans=min(dist2line(p,rec[3],rec[0]),ans); return ans;}Point getPoint(const Point &c,int r,double a){ return Point(c.x+cos(a)*r,c.y+sin(a)*r);}double solve(Point *rec,const Point &c,const Point &p,int R){ double l=0,r=pi; while(l+eps<=r) { double mid=(l+r)/2; double midmid=(mid+r)/2; Point p1=getPoint(c,R,mid),p2=getPoint(c,R,midmid); double dis1=dist2rec(p1,rec)+length(p-p1); double dis2=dist2rec(p2,rec)+length(p-p2); if(dis1>dis2) l=mid; else r=midmid; } Point p0=getPoint(c,R,l); double ans1=dist2rec(p0,rec)+length(p-p0); l=pi,r=2*pi; while(l+eps<=r) { double mid=(l+r)/2; double midmid=(mid+r)/2; Point p1=getPoint(c,R,mid),p2=getPoint(c,R,midmid); double dis1=dist2rec(p1,rec)+length(p-p1); double dis2=dist2rec(p2,rec)+length(p-p2); if(dis1>dis2) l=mid; else r=midmid; } p0=getPoint(c,R,l); double ans2=dist2rec(p0,rec)+length(p-p0); return min(ans1,ans2);}int main(){ double x,y,r; //freopen("4544.in","r",stdin); while(scanf("%lf%lf",&x,&y)!=EOF) { if(x==0.0&&y==0.0) break; Point p(x,y); scanf("%lf%lf%lf",&x,&y,&r); Point c(x,y); Point rec[4]; double x1,y1; scanf("%lf%lf%lf%lf",&x,&y,&x1,&y1); rec[0]=Point(x,y),rec[1]=Point(x1,y); rec[2]=Point(x1,y1),rec[3]=Point(x,y1); printf("%.2lf\n",solve(rec,c,p,r)); } return 0;}
</pre><p></p><p>例2 poj(3301) <a target=_blank href="http://http://poj.org/problem?id=3301">Texas Trip</a></p><p> 给你不超过30个点,问这些点的最小包围正方形。角度的枚举范围在0-PI,然后根据坐标旋转公式求出旋转后的点,然后统计一下。。。</p><p><pre name="code" class="cpp">#include <cmath>#include <cstdio>#include <cstring>#include <iostream>using namespace std;const int maxn=34;const double pi=acos(-1.0);const double inf=1<<30;const double eps=1e-12;struct Point{ double x,y; Point(double x=0,double y=0) :x(x),y(y) { }}p[maxn];double check(double angle,int n){ double minx=inf,miny=inf,maxy=-inf,maxx=-inf; double tcos=cos(angle),tsin=sin(angle); for(int i=0;i<n;i++) { double tx=tcos*p[i].x-tsin*p[i].y; double ty=tsin*p[i].x+tcos*p[i].y; minx=min(minx,tx),miny=min(miny,ty); maxx=max(maxx,tx),maxy=max(maxy,ty); } return max(maxx-minx,maxy-miny);}int main(){ int t,n; //freopen("3301.in","r",stdin); scanf("%d",&t); while(t--) { scanf("%d",&n); for(int i=0;i<n;i++) scanf("%lf%lf",&p[i].x,&p[i].y); double l=0,r=pi; while(l+eps<r) { double mid=(l+r)/2; double midmid=(mid+r)/2; double d1=check(mid,n),d2=check(midmid,n); if(d1<=d2) r=midmid; else l=mid; } double ans=check(l,n); printf("%.2f\n",ans*ans); } return 0;}
例3:hdu(3400) Line belt
给出两条线段,ab,cd,问从a点到d点的最短时间。不难想到,最优策略是在ab上走一段,然后走到cd上某一点,然后走向d点。ab上的时间函数是单调递增,从ab上某点到d点的时间函数是凹函数,因此可以三分ab上的点,确定了这个点之后,三分cd上的那一点,三分套三分。
#include <cmath>#include <cstdio>#include <cstring>#include <iostream>using namespace std;const double eps=1e-8;double inf=1<<26;struct Point{ double x,y; Point(double x=0,double y=0) :x(x),y(y) { }};Point getPoint(){ double x,y; scanf("%lf%lf",&x,&y); return Point(x,y);}Point a,b,c,d;double vab,vcd,vo;double distan(const Point &a,const Point &b){ return sqrt( (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y) );}double getLength(double fir,double sec){ Point p1,p2; p1.x=a.x+(b.x-a.x)*fir; p1.y=a.y+(b.y-a.y)*fir; p2.x=c.x+(d.x-c.x)*sec; p2.y=c.y+(d.y-c.y)*sec; return distan(a,p1)/vab+distan(p1,p2)/vo+distan(p2,d)/vcd;}double solve_cd(double fir){ double l=0,r=1; while(l+eps<=r) { double mid=(l+r)/2; double midmid=(mid+r)/2; double ans1=getLength(fir,mid); double ans2=getLength(fir,midmid); if(ans1<=ans2) r=midmid; else l=mid; } return getLength(fir,l);}double solve_abcd(){ double l=0,r=1; while(l+eps<=r) { double mid=(l+r)/2; double midmid=(mid+r)/2; double ans1=solve_cd(mid); double ans2=solve_cd(midmid); if(ans1<=ans2) r=midmid; else l=mid; } return solve_cd(l);}int main(){ int t; //freopen("3400.in","r",stdin); scanf("%d",&t); while(t--) { a=getPoint(),b=getPoint(); c=getPoint(),d=getPoint(); scanf("%lf%lf%lf",&vab,&vcd,&vo); printf("%.2lf\n",solve_abcd()); } return 0;}
0 0
- 三分法小结
- 三分法
- 三分法
- 三分法
- 三分法
- 三分法
- 三分法
- 三分法
- 三分法
- 三分法
- 三分法
- 三分法
- 三分法
- 三分法
- 二分法、三分法
- 三分法模板
- zoj3203(三分法)
- hdu4445(三分法)
- 索引index
- 非发热管热个人共同如果和他人很突然后天以后
- 求俩整数的差值
- PHP函数implode和explode
- table的编辑模式(多选,插入,删除)
- 三分法小结
- CSS(margin)问题嵌套div中margin-top转移
- 嵌入式 Linux下修改MAC地址
- poj 2096 Collecting Bugs (概率dp)
- 动画
- Eclipse启动时卡死在"Android SDK: Resolving error markers"的解决方法
- Linux系统cp: omitting directory `XXX'问题解决
- table有左右两个btn,并且不止一段的时候,怎么设置tag值
- Mybatis初探之环境的配置