HYSBZ/BZOJ 1038 [ZJOI2008] 瞭望塔 - 计算几何

题目描述

分析:

题目中说的“可以看见”means 在折线的每段所在直线朝x轴正方向,直线的左边一片区域(其实就是折线的每段所在直线的上面)
所以我们要求的就是此题折线所在直线相交构成的一个底朝下的凸壳(类似二次函数a>0的样子),网上都说这个是求半平面交,这里就不那么高大上了,直接求吧。

Solution :

  1. 先对Lines按照斜率大小排序,用一个栈来维护形成凸壳的直线。
    • 如果当前直线与stack [top-2] (栈的倒数第二个元素)的交点横坐标小于stack [top-1]与stack [top-2] 的交点横坐标,那么stack [top-1]这个元素出队(依据是:当前的直线相对倒数第一条直线对倒数第二条直线更优,就不要倒数第一条了)
    • 特别注意如果有斜率相等的直线要取与y轴截距最大的那条,而且不能算交点!!
  2. 因为求得是两段折线之间的距离,显然距离的计算可以表示为分段函数,而每一段函数都是一次函数的形式,那么距离也应该是一次函数的形式(或者常函数),极值都是在折线的交点(折点)处取。
    分为两个部分算。
    • Part 1:凸壳折点对应的距离
    • Part 2:题目给出的折线的折点对应的距离。
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
#define MAXN 300
const double eps=1e-12;
const double INF=1e20;

struct Point{
    int x,y;
}a[MAXN+10];
struct Line{
    double k,b;
}L[MAXN+10],stak[MAXN+10];

int n,top;
double ans=INF,cro[MAXN+10];

double Getk(Point a,Point b){
    return 1.0*(a.y-b.y)/(a.x-b.x);
}
void read()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i].x);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i].y);
    for(int i=1;i<n;i++){
        L[i].k=Getk(a[i],a[i+1]);
        L[i].b=1.0*a[i].y-1.0*a[i].x*L[i].k;
    }
}
bool cmp(Line a,Line b){
    if(fabs(a.k-b.k)<eps) return a.b<b.b;
    return a.k<b.k;
}
double GetCrossP(Line a,Line b){ //Get Crossover Point
    return (a.b-b.b)/(b.k-a.k);
}
void GetConvex()
{
    sort(L+1,L+n,cmp);
    stak[top++]=L[1];
    for(int i=2;i<n;i++){
        while(top){
            if(fabs(stak[top-1].k-L[i].k)<eps) top--;
            else if(top>1&&GetCrossP(L[i],stak[top-2])<=GetCrossP(stak[top-2],stak[top-1]))
                top--;
            else
                break;
        }
        stak[top++]=L[i];
    }
}
double Gety(Line a,double x0){
    return a.k*x0+a.b;
}
void Getans()
{
    //Part 1
    for(int i=1;i<top;i++){
        double x0=GetCrossP(stak[i-1],stak[i]);
        cro[i]=x0;
        int k=-1;
        for(int j=1;j<n;j++)
            if(x0<=1.0*a[j+1].x){
                k=j;
                break;
            }
        if(k==-1) continue;
        Line t;
        t.k=Getk(a[k],a[k+1]);
        t.b=1.0*a[k].y-1.0*a[k].x*t.k;
        double tmp=Gety(stak[i],x0)-Gety(t,x0);
        if(tmp>=0.0)
            ans=min(ans,tmp);
    }
    //Part 2
    for(int i=1;i<=n;i++){
        int k=-1;
        for(int j=1;j<top;j++)
            if(1.0*a[i].x<=cro[j]){
                k=j-1;
                break;
            }
        if(k==-1) continue;
        double tmp=Gety(stak[k],1.0*a[i].x)-1.0*a[i].y;
        if(tmp>=0.0)
            ans=min(ans,tmp);
    }
    printf("%.3lf\n",ans);
}
int main()
{
    read();
    GetConvex();
    Getans();
    return 0;
}
posted @ 2016-02-05 18:20  KatarinaYuan  阅读(176)  评论(0编辑  收藏  举报