BZOJ1038 瞭望塔

看到题 , 既没有想到半平面交 , 也没有想到模拟退火。 弱弱的感觉就是一个数学小模拟题……

解法基于一个猜想: 每条线段上的答案是单峰的……
那么愉快的三分吧…… 注意精度 , 我的代码里1e9是一位都不能动的:-)

代码后我将给出证明

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <string>
#include <vector>
#include <deque>
#include <stack>
#include <algorithm>

using namespace std;
const int maxn = 310;
const double INF = 1e20;

inline int re() { int n; scanf("%d",&n); return n; }
int n;  
double x[maxn];
double y[maxn];

double cal(int i , double xi)
{
    double res = 0;
    double len = y[i]+(y[i+1]-y[i])/(x[i+1]-x[i])*(xi-x[i]);
    for(int j=0;j<n;j++) if(j!=i && j!=i+1)
    {
        int a = j+(j<=i?+1:-1);
        double h = y[j]+(y[a]-y[j])/(x[a]-x[j])*(xi-x[j]);
        res = max(res , h-len);
    }
    return res;
}

int main(int argc, char *argv[]) {

    n = re();
    for(int i=0;i<n;i++) x[i] = re();
    for(int i=0;i<n;i++) y[i] = re();

    double Min = INF;
    for(int i=0;i<n-1;i++) 
    {
        double l = x[i] , r = x[i+1];
        while(fabs(r-l)>1e-9)
        {
            double len = (r-l)/3.0;
            double lm = l+len;
            double rm = r-len;

            if(cal(i, lm)>cal(i, rm)) l = lm;
            else r = rm;
        }
        Min = min(Min , cal(i, l));
    }

    if(n==1) Min = 0;
    printf("%.3lf\n", Min);
    return 0;
}

单峰的证明:

  1. 如果要求一条线段可见 , 那么瞭望塔一定在这条线段所对应的直线的上方。
  2. 当我们讨论瞭望塔的位置在 i i+1 之间时 , 其他的直线可以组成一个下凸的半平面 , 将整个图形旋转使得直线水平 , 可治下凸的半平面仍保持其性质。 那么瞭望塔的高度在此线段上保持单峰性

你可能感兴趣的:(数学,三分,bzoj)