[ZJOI2008]瞭望塔

[ZJOI2008]瞭望塔

题目描述:

致力于建设全国示范和谐小村庄的H村村长dadzhi,决定在村中建立一个瞭望塔,以此加强村中的治安。
我们将H村抽象为一维的轮廓。如下图所示

我们可以用一条山的上方轮廓折线(x1, y1), (x2, y2), …. (xn, yn)来描述H村的形状,这里x1 < x2 < …< xn。瞭望塔可以建造在[x1, xn]间的任意位置, 但必须满足从瞭望塔的顶端可以看到H村的任意位置。可见在不同的位置建造瞭望塔,所需要建造的高度是不同的。为了节省开支,dadzhi村长希望建造的塔高度尽可能小。
请你写一个程序,帮助dadzhi村长计算塔的最小高度。

输入格式:

输入文件tower.in第一行包含一个整数n,表示轮廓折线的节点数目。接下来第一行n个整数, 为x1 ~ xn. 第三行n个整数,为y1 ~ yn。

输出格式:

输出文件tower.out仅包含一个实数,为塔的最小高度,精确到小数点后三位。

输入样例#1:

6
1 2 4 5 6 7
1 2 2 4 2 1

输出样例#1:

1.000

说明:

对于60%的数据, N ≤ 60;
对于100%的数据, N ≤ 300,输入坐标绝对值不超过106,注意考虑实数误差带来的问题。

题解:

第一道半平面交题目,终于ac了。
题解很容易想出,因为要能看到所有的点,那么将所有直线左半平面求交,即合法的点就在所求的交的平面内。根据单调性,最优点一定位于半平面交的交点或地形的交点上(自行脑补,不做解释)那么O(n)求解即可。

代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;

const int max_n = 301;
const long long inf = 1e10;
const double eps = 1e-10;

struct point
{
    double x,y;
    point(double xx=0,double yy=0)
    {
        x=xx; y=yy;
    }
}ploy[max_n],nowploy[max_n],a[max_n>>1],l[max_n>>1];

struct line
{
    point p,v;
    line(point pp=point(0,0),point vv=point(0,0))
    {
        p=pp; v=vv;
    }
};

double ans=inf;
int n,cnt,t;

point operator + (point a,point b) {return point(a.x+b.x,a.y+b.y);}
point operator - (point a,point b) {return point(a.x-b.x,a.y-b.y);}
point operator * (point a,double b) {return point(a.x*b,a.y*b);}
point operator / (point a,double b) {return point(a.x/b,a.y/b);}
inline double dot(point a,point b) {return a.x*b.x+a.y*b.y;}
inline double cross(point a,point b) {return a.x*b.y-a.y*b.x;}

inline double dcmp(double x)
{
    if(fabs(x)<eps) return 0;
    else return x<0 ? -1 : 1;
}

inline bool cmp(point a,point b)
{
    if(a.x<b.x) return true;
    else return false;
}

inline double get_long(line a,point b)
{
    double k=a.v.y/a.v.x;
    return fabs((k*b.x+a.p.y-a.p.x*k)-b.y); 
}

inline void init_cut()
{
    ploy[++cnt]=point(inf,inf);
    ploy[++cnt]=point(-inf,inf);
    ploy[++cnt]=point(-inf,-inf);
    ploy[++cnt]=point(inf,-inf);
}

inline bool jud_inter(line now,point c,point d)
{
    point v=now.v,w=c-now.p,u=d-now.p;
    int x=dcmp(cross(v,w)),y=dcmp(cross(v,u));
    return x!=y && x!=0 && y!=0;
}

inline point get_inter(point a,point b,point c,point d)
{
    point u=a-c;
    double v=cross(d,u)/cross(b,d);
    return a+b*v;
}

inline void get_line(int i)
{
    point c=ploy[i],d=ploy[i+1];
    if(dcmp(c.x-d.x)==0)
      a[i].x=inf,a[i].y=0;
    else
    {
      a[i].x=(c.y-d.y)/(c.x-d.x);
      a[i].y=a[i].x*c.x+c.y;
    }
}

inline void cutploy(point a,point b)
{
    int nowcnt=0;
    ploy[cnt+1]=ploy[1];

    for(int i=1; i<=cnt; ++i)
    {
        point c=ploy[i],d=ploy[i+1];
        if(dcmp(cross(b-a,c-a))>=0) nowploy[++nowcnt]=c;
        line now=line(a,b-a);
        if(jud_inter(now,c,d))
          nowploy[++nowcnt]=get_inter(c,d-c,a,b-a);
    }

    cnt=nowcnt;
    for(int i=1; i<=nowcnt; ++i)
      ploy[i]=nowploy[i];
}

inline void output_answer()
{
    int k1=1,k2=1;
    sort(ploy+1,ploy+cnt+1,cmp);
    while(k1<=n && k2<=cnt)
    {
        if(l[k1].x<ploy[k2].x)
        {
            ans=min(ans,get_long(line(ploy[k2-1],ploy[k2]-ploy[k2-1]),l[k1]));
            k1++;
        }
        else 
        {
            ans=min(ans,get_long(line(l[k1-1],l[k1]-l[k1-1]),ploy[k2]));
            k2++;
        }
    }
}

int main()
{
    scanf("%d",&n); init_cut();
    for(int i=1; i<=n; ++i)
      scanf("%lf",&l[i].x);
    for(int i=1; i<=n; ++i)
      scanf("%lf",&l[i].y);
    for(int i=2; i<=n; ++i)
      cutploy(l[i-1],l[i]);
    output_answer();
    printf("%.3lf\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值