[模拟退火 二分答案] BZOJ 1038 [ZJOI2008]瞭望塔


正解半平面交,%%% PoPoQQQ : http://blog.csdn.net/popoqqq/article/details/39340759

"确定瞭望塔的高度的时候我们选择二分处理 对于二分的每一个值 我们把折线上的端点从左到右枚举 瞭望塔的塔尖到每个端点的连线必须从左到右逆时针顺序 否则就会被遮挡"

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<ctime>
#define eps 1e-4
using namespace std;

inline char nc()
{
	static char buf[100000],*p1=buf,*p2=buf;
	if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
	return *p1++;
}

inline void read(int &x){
	char c=nc(),b=1;
	for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
	for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

inline int dcmp(double a,double b){
	if (fabs(a-b)<1e-5) return 0;
	if (a<b) return -1;
	return 1;
}

struct Point{
	double x,y;
	Point(double x=0,double y=0):x(x),y(y) { }
	friend double Cross(Point p1,Point p2,Point p0){
		return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y);
	}
}P[305];

int n;
double x1,xn;
double ans=1e130;

inline double rnd(){
	return (rand()%1000)/1000.0;
}

inline double calc(double x){
	for (int i=1;i<n;i++)
		if (dcmp(P[i].x,x)<=0 && dcmp(x,P[i+1].x)<=0)
			return P[i].y+(P[i+1].y-P[i].y)/(P[i+1].x-P[i].x)*(x-P[i].x);
}

Point Q[305];
int r;

inline int C(Point E){
	r=0;
	for (int i=1;i<=n;i++)
	{
		while (r && dcmp(Cross(P[i],Q[r],E),0)>=0) r--;
		Q[++r]=P[i];
	}
	return r;
}

inline bool Check(Point E)
{
	for(int i=2;i<=n;i++)  
		if(dcmp(Cross(P[i-1],P[i],E),0)<0)  
			return 0;  
	return 1;
} 

inline double Bin(double x){
	double L=0,R=1e11,MID;
	double y=calc(x);
	while (R-L>1e-5)
		if (Check(Point(x,y+(MID=(L+R)/2))))
			R=MID;
		else
			L=MID;
	ans=min(ans,(L+R)/2);
	return (L+R)/2;
}

inline void SA(){
	#define beta .99
	#define EPS 1e-5 
	double nx,sx,now,delta,ret;
	sx=(x1+xn)/2;
	now=Bin(sx);
	for (double T=xn-x1;T>EPS;T*=beta){
		nx=sx+T*(rnd()*2-1);
		if(nx<x1 || nx>xn) continue;   
		ret=Bin(nx);
		delta=now-ret;
		if (delta>0 || rnd()<exp(delta/T))
			sx=nx,now=ret;
	}
}

int main()
{
	#define Case 2
	srand(10086);  
	int ix;
	freopen("t.in","r",stdin);
	freopen("t.out","w",stdout);
	read(n);
	for (int i=1;i<=n;i++) read(ix),P[i].x=ix;
	for (int i=1;i<=n;i++) read(ix),P[i].y=ix;
	x1=P[1].x; xn=P[n].x;
	for (int i=1;i<=Case;i++)
		SA();
	printf("%.3lf\n",ans);
	return 0;
}


你可能感兴趣的:([模拟退火 二分答案] BZOJ 1038 [ZJOI2008]瞭望塔)