bzoj1038: [ZJOI2008]瞭望塔

Description

  致力于建设全国示范和谐小村庄的H村村长dadzhi,决定在村中建立一个瞭望塔,以此加强村中的治安。我们
将H村抽象为一维的轮廓。如下图所示 我们可以用一条山的上方轮廓折线(x1, y1), (x2, y2), …. (xn, yn)来描
述H村的形状,这里x1 < x2 < …< xn。瞭望塔可以建造在[x1, xn]间的任意位置, 但必须满足从瞭望塔的顶端可
以看到H村的任意位置。可见在不同的位置建造瞭望塔,所需要建造的高度是不同的。为了节省开支,dadzhi村长
希望建造的塔高度尽可能小。请你写一个程序,帮助dadzhi村长计算塔的最小高度。

Input

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

Output

  仅包含一个实数,为塔的最小高度,精确到小数点后三位。

Sample Input

【输入样例一】
6
1 2 4 5 6 7
1 2 2 4 2 1
【输入样例二】
4
10 20 49 59
0 10 10 0

Sample Output

【输出样例一】
1.000
【输出样例二】
14.500

HINT

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


题解:

说实话一开始没看出是半平面交,后来看题解的图才发现。

看图yy一下可以发现,每相邻两个点组成的所有直线,若想要塔可以看到所有点,塔必须在凸壳上,或者在山顶。

所以我们先求出凸壳,在枚举凸壳上的点到村庄的距离。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#define eps 1e-11
#define inf 1000000000
const int N=1105;
using namespace std;
int n;
struct node{
	double x,y;
}p[N],sb[N];int pl;
struct node2{
	node p1,p2;
    double angle;
    node2(){}
    node2(double x1,double y1,double x2,double y2)
    {
        p1.x=x1;p1.y=y1;p2.x=x2;p2.y=y2;
    }
    void get_angle()
    {
        angle=atan2(p2.y-p1.y,p2.x-p1.x);
    }
}seg[N],sa[N],ss[N];int cnt=0,st,ed;
void init()
{
	p[0].x=p[1].x;p[0].y=100001;
	p[n+1].x=p[n].x;p[n+1].y=100001;
	for(int i=1;i<=n;i++)
	{
		seg[++cnt]=node2(p[i].x,p[i].y,p[i+1].x,p[i+1].y),seg[cnt].get_angle();
		seg[++cnt]=node2(p[i-1].x,p[i-1].y,p[i].x,p[i].y),seg[cnt].get_angle();
	}
}
double mulit(node p1,node p2,node p0)
{
    double x1=p1.x-p0.x,y1=p1.y-p0.y;
    double x2=p2.x-p0.x,y2=p2.y-p0.y;
    return x1*y2-x2*y1;
}
bool satify(node x,node2 y)
{
    if(mulit(x,y.p2,y.p1)<=eps) return true;return false;
}
bool cmp(node2 x,node2 y)
{
    if(x.angle<y.angle) return true;
    if(fabs(x.angle-y.angle)<eps&&satify(x.p1,y)==true) return true;
    return false;
}
node jd(node2 x,node2 y)
{
    node p1=x.p1,p2=x.p2,p3=y.p1,p4=y.p2,p;
    double t1=mulit(p1,p4,p3);
    double t2=mulit(p2,p4,p3);
    p.x=(t1*p2.x-t2*p1.x)/(t1-t2);
    p.y=(t1*p2.y-t2*p1.y)/(t1-t2);
    return p;
}
void work()
{
	sort(seg+1,seg+cnt+1,cmp);
    int tp=1;
    for(int i=2;i<=cnt;i++)
    if(seg[i].angle-seg[tp].angle>eps) seg[++tp]=seg[i];
    cnt=tp;
    sa[1]=seg[1];sa[2]=seg[2];
    st=1,ed=2;
    for(int i=3;i<=cnt;i++)
    {
        while(st<ed&&satify(jd(sa[ed],sa[ed-1]),seg[i])==false) ed--;
        while(st<ed&&satify(jd(sa[st],sa[st+1]),seg[i])==false) st++;
        sa[++ed]=seg[i];
    }
    while(st<ed&&satify(jd(sa[ed],sa[ed-1]),seg[st])==false) ed--;
    while(st<ed&&satify(jd(sa[st],sa[st+1]),seg[ed])==false) st++;
    pl=0;
    for(int i=st;i<ed;i++) sb[++pl]=jd(sa[i],sa[i+1]);
}
void get_ans()
{
	double ans=1e60;
	for(int k=1;k<=pl;k++)
		for(int i=1;i<n;i++)
		{
			node t;t.x=sb[k].x;t.y=-1;
			if(sb[k].x>=p[i].x&&sb[k].x<=p[i+1].x)
			{
				
				ss[1]=node2(p[i].x,p[i].y,p[i+1].x,p[i+1].y);
				ss[2]=node2(t.x,t.y,sb[k].x,sb[k].y);
				 ans=min(ans,sb[k].y-jd(ss[1],ss[2]).y);//printf("%.2lf\n",ans);
			}
			   
		}
	for(int k=1;k<=n;k++)
		for(int i=1;i<pl;i++)
		{
			node t;t.x=p[k].x;t.y=-1;
			if(p[k].x>=sb[i].x&&p[k].x<=sb[i+1].x)
			{    
				ss[2]=node2(t.x,t.y,p[k].x,p[k].y);
				ss[1]=node2(sb[i].x,sb[i].y,sb[i+1].x,sb[i+1].y);
				ans=min(ans,jd(ss[1],ss[2]).y-p[k].y);
			}
			    
		}
	printf("%.3lf",ans);
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%lf",&p[i].x);
	for(int i=1;i<=n;i++) scanf("%lf",&p[i].y);
	init();
	work();
	get_ans();
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值