bzoj1038: [ZJOI2008]瞭望塔

【题目网址】

  http://www.lydsy.com/JudgeOnline/problem.php?id=1038

【我的题解】

  半平面交,学了三天aaaaaaa

  一开始只有一本ACM教程,那上面根本说的不全(预处理只字未提),后来多亏了hzwer的悉心解答,才搞懂了

  关于半平面交我可能在以后写个总结

  这道题就是先半平面交,求出一个凸包,对于凸包上的每个点,做一条平行于y轴的直线,交下面的轮廓于一个点,用这个点和你枚举的点的距离更新ans。对于轮廓的折点,也做同样的处理,用距离更新ans。(具体为什么画个图就知道了)

【我的代码】

//半平面交
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <iostream>
#define maxn 400
#define inf 1e10
using namespace std;
int N, cnt;
struct point{double x,y;}p[maxn], sur[maxn];
struct vec
{
	point a, b;
	double k;
	double operator*(vec v)
	{
		double x1=b.x-a.x, x2=v.b.x-v.a.x, y1=b.y-a.y, y2=v.b.y-v.a.y;
		return x1*y2-x2*y1;
	}
}q[maxn], seg[maxn];
void init()
{
	int i;
	scanf("%d",&N);
	for(i=1;i<=N;i++)scanf("%lf",&p[i].x);
	for(i=1;i<=N;i++)scanf("%lf",&p[i].y);
	for(i=1;i<N;i++)seg[i]=(vec){p[i],p[i+1]};
	seg[N]=(vec){(point){-inf,inf},(point){-inf,-inf}};
	seg[N+1]=(vec){(point){-inf,-inf},(point){inf,-inf}};
	seg[N+2]=(vec){(point){inf,-inf},(point){inf,inf}};
	seg[N+3]=(vec){(point){inf,inf},(point){-inf,inf}};
	cnt=N+3;
	for(i=1;i<=cnt;i++)
		seg[i].k=atan2((seg[i].b.y-seg[i].a.y),(seg[i].b.x-seg[i].a.x));
}
bool in(point p, vec v)
{
	vec vv=(vec){v.a,p};
	return vv*v<0;
}
bool cmp(vec v1, vec v2)
{return v1.k==v2.k?in(v1.a,v2):v1.k<v2.k;}
point cross(vec v1, vec v2)
{
	double k1, k2, b1, b2, x, y;
	k1=(v1.a.y-v1.b.y)/(v1.a.x-v1.b.x);
	k2=(v2.a.y-v2.b.y)/(v2.a.x-v2.b.x);
	b1=-k1*v1.a.x+v1.a.y;
	b2=-k2*v2.a.x+v2.a.y;
	if(v1.a.x==v1.b.x)x=v1.a.x, y=k2*x+b2;
	else if(v2.a.x==v2.b.x)x=v2.a.x, y=k1*x+b1;
	else x=(b1-b2)/(k2-k1), y=k1*x+b1;
	return (point){x,y};
}
void work()
{
	int i, l, r, x;
	sort(seg+1,seg+cnt+1,cmp);
	for(i=2,x=1;i<=cnt;i++)if(seg[i].k!=seg[i-1].k)seg[++x]=seg[i];
	cnt=x;
	q[l=1]=seg[1];
	q[r=2]=seg[2];
	for(i=3;i<=cnt;i++)
	{
		while(l<r and !in(cross(q[r],q[r-1]),seg[i]))r--;
		while(l<r and !in(cross(q[l],q[l+1]),seg[i]))l++;
		q[++r]=seg[i];
	}
	while(l<r and !in(cross(q[r],q[r-1]),q[l]))r--;
	while(l<r and !in(cross(q[l],q[l+1]),q[r]))l++;
	cnt=0;
	for(i=l;i<r;i++)sur[++cnt]=cross(q[i],q[i+1]);
	sur[0]=cross(q[r],q[l]);
}
void getans()
{
	int i, j;
	double ans=inf;
	point t;
	for(i=1;i<cnt;i++)
		for(j=1;j<N;j++)
			if(sur[i].x>=p[j].x and sur[i].x<=p[j+1].x)
			{
				t=cross((vec){sur[i],(point){sur[i].x,inf}},(vec){p[j],p[j+1]});
				ans=min(ans,sur[i].y-t.y);
			}
	for(i=1;i<=N;i++)
		for(j=0;j<cnt;j++)
			if(p[i].x>=sur[j].x and p[i].x<=sur[j+1].x)
			{
				t=cross((vec){p[i],(point){p[i].x,inf}},(vec){sur[j],sur[j+1]});
				ans=min(ans,t.y-p[i].y);
			}
	printf("%.3lf\n",ans);
}
int main()
{
	init();
	work();
	getans();
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值