一道很棒的题,思维难度不大,但是很考实现细节。
做了此题发现计算几何有点入门了。
#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;
}