bzoj1038

一道很棒的题,思维难度不大,但是很考实现细节。

做了此题发现计算几何有点入门了。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
const double eps = 1e-8;
double cc(double a)
{
	if (fabs(a) < eps)
		return 0;
	else
		return a;
}
struct pointt
{
	double x, y;
	pointt(double x, double y) :x(x), y(y)
	{}
	pointt(){}
};
struct linee
{
	double a, b;
	pointt start, end;
	linee(pointt aa, pointt bb)
	{
		a = cc(aa.y - bb.y) / (aa.x - bb.x);
		b = cc(bb.y - a*bb.x);
		start = aa, end = bb;
	}
	linee(){}
};
bool comm(linee a, linee b)
{
	if (fabs(a.a - b.a) < eps)
		return a.b > b.b;
	return a.a < b.a;
}
struct com
{
	pointt a;
	int l, r;
	com()
	{
		l = -1, r = -1;
	}
};
com p[500],getp[500];
linee l[5000],templ[5000];
linee getl[5000];
int n,tot,totp;
int first, last;
//二分的关键是 l r取值要交叉即若r=mid 那么l只能是mid+1,像l=mid,r=mid-1这中情况重合了就会导致死循环,若判断最大值用mid+1比较若是最小值用mid比较。
//这里写什么bound的时候因为r>=x是恒成立的如果数列中没有等于x的数那么r是刚好大于x的但由于写得是lower_bound所以要特判一下;
int lower_bound(com a,com *p,int l,int r)
{
	while (l < r)
	{
		int mid = (l + r) >> 1;
		if (p[mid].a.x>=a.a.x)
		{
			r= mid;
			
		}
		else
			l = mid+1;
	}
	return p[r].a.x>a.a.x?r-1:r;
}
pointt operator+(pointt a, pointt b)
{
	return pointt(a.x + b.x, a.y + b.y);
}
pointt operator-(pointt a, pointt b)
{
	return pointt(a.x - b.x, a.y - b.y);
}
double  operator*(pointt a, pointt b)
{
	return a.x*b.x + a.y*b.y;
}
double dot(pointt a, pointt b)
{
	return a.x*b.y - a.y*b.x;
}
bool isonleft(pointt a, linee b)
{
	double y = b.a*a.x + b.b;
	return a.y > y;
}
pointt inter(linee a, linee b)
{
	double x = (b.b - a.b) / (a.a - b.a);
	double y = a.a*x + a.b;
	pointt c = pointt(x, y);
	return c;
}
//getl里的a b代表 y=ax+b getp里的l r代表左右直线的编号 a代表左右直线的交点
void halfmat()
{
	first = last = 1;
	getl[first] = l[1];
	for (int i = 2; i < tot; i++)
	{
		if (fabs(l[i].a - getl[last].a) < eps)continue;//处理平行情况
		while (last - first >= 1 && !isonleft(inter(getl[last], getl[last - 1]), l[i]))last--;
		while (last - first >= 1 && !isonleft(inter(getl[first], getl[first + 1]), l[i]))first++;
		last++;
		getl[last] = l[i];//此题特殊不用进行最后的缝合。
	}
	getp[totp].l = first; getp[totp].r = first;
	getp[totp++].a = pointt(p[1].a.x, getl[first].a*p[1].a.x + getl[first].b);//这是为了以后lower_bound找的时候不会出问题
	for (int i = first + 1; i <= last; i++)
	{
		getp[totp].l = i - 1; getp[totp].r = i;
		getp[totp++].a = inter(getl[i], getl[i - 1]);
	}
	getp[totp].l = last; getp[totp].r = last;
	getp[totp++].a = pointt(p[n].a.x, getl[last].a*p[n].a.x + getl[last].b);
}
int main()
{
	scanf("%d", &n);
	tot = 1; totp = 1;
	for (int i = 1; i <= n; i++)
	{
		scanf("%lf", &p[i].a.x);
	}
	for (int i = 1; i <= n; i++)
	{
		scanf("%lf", &p[i].a.y);
	}
	p[1].l = p[1].r = 1;
	for (int i = 2; i <=n; i++)
	{
		l[tot++] = linee(p[i].a, p[i - 1].a);
		p[i].l = tot - 1, p[i - 1].r = tot - 1;
	}
		p[n].l = p[n].r = tot - 1;
		for (int i = 1; i <tot; i++)templ[i] = l[i];//这一定要存储一个临时的因为排序后就变了
	sort(l+1, l + tot, comm);
	halfmat();
	if (last == 1)
		printf("0.000\n");
	else
	{
		double ans = 1000000000000000000;//说是绝对值小于100,评测的时候尼玛有的数据就大了2333
		for (int i = 1; i < totp; i++)
		{
			if (i == 1 || i == totp - 1)continue;//你并不能考虑虚拟节点!!!因为如果第一和第二条线的交点在范围里面或是外面情况是不一样的。
			int st = lower_bound(getp[i], p,1,n);
			double y = -1000000000;
			if(getp[i].a.x<=p[n].a.x&&getp[i].a.x>=p[0].a.x) //这一定要排除隐患
				y = templ[p[st].r].a*getp[i].a.x + templ[p[st].r].b;
			ans = min(ans, cc(getp[i].a.y - y));
		}
		for (int i = 1; i <=n; i++)
		{
			int st = lower_bound(p[i], getp,1,totp-1);
			double y = -1000000000;
			y = getl[getp[st].r].a*p[i].a.x + getl[getp[st].r].b;
			ans = min(ans, cc(y - p[i].a.y));//这一定要进行误差分析!!!!
		}
		printf("%.3lf\n", ans);
	}
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值