【BZOJ】【P1038】【ZJOI2008】【瞭望塔】【题解】【二分+模拟退火】

来源:互联网 发布:mac 环境变量 编辑:程序博客网 时间:2024/05/21 10:49

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1038

一开始以为只能在顶点修,然后就开开心心枚举+二分,发现样例都过不了= = 、

然后又不会半平面交,那就模拟退火骗分吧,开开心心写了模拟退火,拿来数据一测, 30 ,然后又开开心心的对着数据调参数= =、

Code:

#include<bits/stdc++.h>#define rnd ((double)rand()/RAND_MAX)using namespace std;const int maxn=305;const double eps=1e-7;int dcmp(double x){    if(fabs(x)<eps)return 0;    return x>0?1:-1;}double x[maxn],y[maxn];int n;bool ok(double X,double h){    double Y;    int pre,suc;    suc=lower_bound(x+1,x+1+n,X)-x;    pre=suc-1;    Y=y[pre]+(X-x[pre])*(y[suc]-y[pre])/(x[suc]-x[pre]);    for(int i=2;i<=pre;i++){        if((Y+h-y[i])*(x[i]-x[i-1])<(y[i]-y[i-1])*(X-x[i]))return 0;    }    for(int i=suc;i<n;i++){        if((y[i]-Y-h)*(x[i+1]-x[i])>(y[i+1]-y[i])*(x[i]-X))return 0;    }return 1;}double ans=1e11;double getans(double X){    double l=0,r=1e11;    int m=200;    while(dcmp(l-r)&&m){        m--;        double mid=(l+r)*.5;        if(ok(X,mid))            r=mid;        else            l=mid;    }ans=min(ans,l);    return l;   }int main(){    srand(10086);    scanf("%d",&n);    for(int i=1;i<=n;i++)scanf("%lf",x+i);    for(int i=1;i<=n;i++)scanf("%lf",y+i);    int m=1;    while(m--){        double X=x[1]+(x[n]-x[1])*rnd;        double an=getans(X);        double T=x[n]-x[1],res;        while(T>1e-8){            T/=2;            if(X+T<=x[n]){                res=getans(X+T);                if(dcmp(res-an)<0){                    an=res;                    X=X+T;                }            }            if(X-T>=x[1]){                res=getans(X-T);                if(dcmp(res-an)<0){                    an=res;                    X=X-T;                }            }            T*=2;            T*=0.96;        }    }    printf("%.3lf\n",ans);    return 0;}


0 0
原创粉丝点击