HDU - 3644:A Chocolate Manufacturer's Problem(模拟退火, 求多边形内最大圆半径)
Posted hua-dong
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDU - 3644:A Chocolate Manufacturer's Problem(模拟退火, 求多边形内最大圆半径)相关的知识,希望对你有一定的参考价值。
pro:给定一个N边形,然后给半径为R的圆,问是否可以放进去。 问题转化为多边形的最大内接圆半径。(N<50);
sol:乍一看,不就是二分+半平面交验证是否有核的板子题吗。 然而事情并没有那么简单。 因为我们的多边形可能是凹多边形,而前面的方法只对凸多边形有效。
学习了下模拟退火的算法,这个随机算法只在最小圆覆盖的时候写过。 这里再学一下,看起来更正宗一点的。 每次在当前点的附近(R)找是否能优化,而这个R慢慢变小,使得趋紧答案的趋势更精细。
判定点再多边形内:同样,不能用检验是否在每条边的左边来判定,因为不是凸多边形; 我们可以用射线法搞。
(rate和次数是抄的别人的,我自己也不会分析。 感觉这个取决于数据和人品吧
(随机要加srand来增加随机性。
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=110; const double pi=acos(-1.0); const double inf=0x7fffffff; struct point double x,y; point() point(double xx,double yy):x(xx),y(yy) ; struct line point s,p; line() line(point xx,point yy):s(xx),p(yy) ; double getdis(point w,point v) return sqrt((w.x-v.x)*(w.x-v.x)+(w.y-v.y)*(w.y-v.y)); point operator /(point a,double t) return point(a.x/t,a.y/t); point operator *(point a,double t) return point(t*a.x,t*a.y); point operator -(point w,point v)return point(w.x-v.x,w.y-v.y); point operator +(point w,point v)return point(w.x+v.x,w.y+v.y); double det(point w,point v) return w.x*v.y-w.y*v.x; double dot(point w,point v) return w.x*v.x+w.y*v.y; double ltoseg(point p,point a,point b) point t=p-a; if(dot(t,b-a)<=0) return getdis(p,a); else if(dot(p-b,a-b)<=0) return getdis(p,b); return fabs(det(t,b-a))/getdis(a,b); point p[maxn],tp[maxn]; double dist[maxn]; int N; line L[maxn]; bool isinside(point a) //算法描述:首先,对于多边形的水平边不做考虑,其次, //对于多边形的顶点和射线相交的情况,如果该顶点时其所属的边上纵坐标较大的顶点,则计数,否则忽略该点, //最后,对于Q在多边形上的情形,直接判断Q是否属于多边形。 int ncross=0; rep(i,0,N-1) point p1=p[i],p2=p[i+1]; if(ltoseg(a,p[i],p[i+1])==0) return true; //在线段上 if(p1.y==p2.y) continue; //默认做水平x轴的线,所以水平线不考虑 if(a.y<min(p1.y,p2.y)) continue; //相离不考虑 if(a.y>max(p1.y,p2.y)) continue; double t=det(a-p[i],a-p[i+1]); if((t>=0&&p[i].y<a.y&&p[i+1].y>=a.y)||(t<=0&&p[i+1].y<a.y&&p[i].y>=a.y)) ncross++; return (ncross&1); double getmindis(point a) double ans=inf; rep(i,0,N-1) ans=min(ans,ltoseg(a,p[i],p[i+1])); return ans; int main() srand(unsigned(time(NULL))); while(~scanf("%d",&N)&&N) double X,Y,R; X=Y=0; rep(i,1,N) scanf("%lf%lf",&p[i].x,&p[i].y); X=max(X,p[i].x); Y=max(Y,p[i].y); p[0]=p[N]; rep(i,0,N-1) L[i]=line(p[i],p[i+1]-p[i]); scanf("%lf",&R); int maxt=min(N,20); rep(i,0,maxt-1) tp[i]=(p[i]+p[i+1])/2; dist[i]=0; double step=min(X,Y); const int maxd=10; const double rate=0.55; bool flag=0; const double EPS2=1e-6; while(step>EPS2&&!flag) rep(i,0,maxt-1) rep(j,0,maxd-1) double d=rand()%360/360.0*2*pi; point next=tp[i]; next.x+=step*sin(d); next.y+=step*cos(d); if(!isinside(next)) continue; double tdis=getmindis(next); if(tdis+EPS2>dist[i]) dist[i]=tdis; tp[i]=next; if(tdis+EPS2>=R) flag=1; break; step*=rate; if(flag) puts("Yes"); else puts("No"); return 0;
以上是关于HDU - 3644:A Chocolate Manufacturer's Problem(模拟退火, 求多边形内最大圆半径)的主要内容,如果未能解决你的问题,请参考以下文章
HDU - 4112 Break the Chocolate(规律)