【BZOJ1038】[ZJOI2008]瞭望塔 半平面交

本文详细解析了BZOJ1038瞭望塔问题,探讨了如何通过计算几何和半平面交算法找到瞭望塔的最小高度,以满足在H村任意位置都能看到的要求。

【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,注意考虑实数误差带来的问题。

题解:首先能看到所有位置的点一定在这些折线的上半平面交上,现在问题就变成了在半平面交和下面的折线上各选择一个横坐标相同的点使得两点间距离最小。因为上下都是折线,那么最小值一定是在某个折线的拐点处取到,那么只需要将这些拐点都扫一遍即可。

为了方便,我们需要添加3条辅助线(上,左,右)来限制边界,注意3条线的斜率不能相同。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long double ld;
const int maxn=310;
const ld eps=1e-10;
int n,m,h,t;
int q[maxn];
ld ans;
struct point
{
	ld x,y;
	point (){}
	point (ld a,ld b){x=a,y=b;}
	point operator + (const point &a) const {return point(x+a.x,y+a.y);}
	point operator - (const point &a) const {return point(x-a.x,y-a.y);}
	ld operator * (const point &a) const {return x*a.y-y*a.x;}
	point operator * (const ld &a) const {return point(x*a,y*a);}
}v[maxn],p[maxn];
struct line
{
	point p,v;
	ld a;
	line() {}
	line(point x,point y) {p=x,v=y,a=atan2(v.y,v.x);}

}l[maxn];
point getp(line l1,line l2)
{
	point u=l1.p-l2.p;
	ld temp=(l2.v*u)/(l1.v*l2.v);
	return l1.p+l1.v*temp;
}
bool onlft(line a,point b)
{
	return a.v*(b-a.p)>eps;
}
bool cmpl(line a,line b)
{
	if(fabs(a.a-b.a)<eps)	return onlft(a,b.p);
	return a.a<b.a;
}
bool cmpp(point a,point b)
{
	return a.x<b.x;
}
void HPI()
{
	sort(l+1,l+n+3,cmpl);
	int i,cnt=0;
	for(i=2,cnt=1;i<=n+2;i++)	if(fabs(l[i].a-l[cnt].a)>eps)	l[++cnt]=l[i];
	h=t=q[1]=1;
	for(i=2;i<=cnt;i++)
	{
		while(h<t&&onlft(l[i],getp(l[q[t]],l[q[t-1]])))	t--;
		while(h<t&&onlft(l[i],getp(l[q[h]],l[q[h+1]])))	h++;
		q[++t]=i;
	}
	while(h<t&&onlft(l[q[h]],getp(l[q[t]],l[q[t-1]])))	t--;
	while(h<t&&onlft(l[q[t]],getp(l[q[h]],l[q[h+1]])))	h++;
	q[++t]=q[h];
	for(i=h;i<t;i++)	p[++m]=getp(l[q[i]],l[q[i+1]]);
	sort(p+1,p+m+1,cmpp);
}
void calc(point x,point a,point b)
{
	point c;
	if(fabs(x.x-a.x)<eps)	c=a;
	else	c=a+(b-a)*((x.x-a.x)/(b.x-a.x));
	ld dis=fabs(c.y-x.y);
	ans=min(ans,dis);
}
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<'0'||gc>'9')	{if(gc=='-')f=-f;	gc=getchar();}
	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
	return ret*f;
}
int main()
{
	n=rd();
	int i,j;
	for(i=1;i<=n;i++)	v[i].x=rd();
	for(i=1;i<=n;i++)	v[i].y=rd();
	for(i=1;i<n;i++)	l[i]=line(v[i+1],v[i]-v[i+1]);
	l[n].p=point(0,1e25),l[n].v=point(1e7,1);
	l[n+1].p=point(-1,0),l[n+1].v=point(1,1e25);
	l[n+2].p=point(1e7,1e25),l[n+2].v=point(-1e25,-2);
	HPI(),ans=1e25;
	for(i=1,j=2;i<=n;i++)
	{
		for(;p[j].x<v[i].x;j++)
			calc(p[j],v[i-1],v[i]);
		if(j>2)
			calc(v[i],p[j-1],p[j]);
	}
	printf("%.3Lf",ans);
	return 0;
}

 

转载于:https://www.cnblogs.com/CQzhangyu/p/7500585.html

Delphi 12.3 作为一款面向 Windows 台的集成开发环境,由 Embarcadero Technologies 负责其持续演进。该环境以 Object Pascal 语言为核心,并依托 Visual Component Library(VCL)框架,广泛应用于各类桌面软件、数据库系统及企业级解决方案的开发。在此生态中,Excel4Delphi 作为一个重要的社区开源项目,致力于搭建 Delphi 与 Microsoft Excel 之间的高效桥梁,使开发者能够在自研程序中直接调用 Excel 的文档处理、工作表管理、单元格操作及宏执行等功能。 该项目以库文件与组件包的形式提供,开发者将其集成至 Delphi 工程后,即可通过封装良好的接口实现对 Excel 的编程控制。具体功能涵盖创建与编辑工作簿、格式化单元格、批量导入导出数据,乃至执行内置公式与宏指令等高级操作。这一机制显著降低了在财务分析、报表自动生成、数据整理等场景中实现 Excel 功能集成的技术门槛,使开发者无需深入掌握 COM 编程或 Excel 底层 API 即可完成复杂任务。 使用 Excel4Delphi 需具备基础的 Delphi 编程知识,并对 Excel 对象模型有一定理解。实践中需注意不同 Excel 版本间的兼容性,并严格遵循项目文档进行环境配置与依赖部署。此外,操作过程中应遵循文件访问的最佳实践,例如确保目标文件未被独占锁定,并实施完整的异常处理机制,以防数据损毁或程序意外中断。 该项目的持续维护依赖于 Delphi 开发者社区的集体贡献,通过定期更新以适配新版开发环境与 Office 套件,并修复已发现的问题。对于需要深度融合 Excel 功能的 Delphi 应用而言,Excel4Delphi 提供了经过充分测试的可靠代码基础,使开发团队能更专注于业务逻辑与用户体验的优化,从而提升整体开发效率与软件质量。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值