P2600 [ZJOI2008]瞭望塔 题解
code_hunter · · 题解
Update 2020/12/27 修复了一点锅。 Update 2020/12/28 删除了不必要的预编译指令。
题目大意
- 给定一条折线
- 求折线正上方一个点,使:
- 点到折线的竖直距离最小;
- 点与任一折点的连线不与折线相交。
前置知识
- 半平面交
解法
- 对于折线上任一一段,该点能看到其端点仅当该点在该线段所在直线的上方。
- 求出所有满足(1)的半平面的交集,显然,这是一个下凸壳。
- 假设存在一个下凸壳上的点 A 与一个折点 B ,使得其连线与折线相交。分情况讨论。
- 交于折点 C (显然 B C 不相邻), 易知 B C 中折线中必有不满足假设者。
- 交于折线 CD , 易知 B C 中折线中必有不满足假设者。
- 如下图(画技不好,请见谅),灰色部分为下凸壳,枚举下凸壳与原折线的折点,可求此时两者间的距离。取min即可。
代码
#include <bits/stdc++.h>
#define For(i, a, b) for (int i = a; i <= b; i++)
#define pss pair<string, string>
typedef long long ll;
typedef long double ldb;
ldb ASDF , FGHJ;
#define MAX(a, b) (ASDF = (a)) < (FGHJ = (b)) ? FGHJ : ASDF
#define MIN(a, b) (ASDF = (a)) > (FGHJ = (b)) ? FGHJ : ASDF
const ldb eps = 1e-9;
const int MAXN = 307;
using namespace std;
int N;
struct Node {
ldb x, y;
bool operator<(Node b) { return x < b.x; }
} a[MAXN], c[MAXN];
struct line {
ldb k, b;
bool operator<(line g) { return k < g.k || (k == g.k && b > g.b); }
} e[MAXN];
int now;
int stk[MAXN], tp;
inline line make_line(Node a, Node b) { // a.x != b.x
register line res;
res.k = (b.y - a.y) / (b.x - a.x);
res.b = a.y - res.k * a.x;
return res;
}
inline Node intersect(line a, line b) {
register Node res;
res.x = (a.b - b.b) / (b.k - a.k);
res.y = a.k * res.x + a.b;
return res;
}
ldb ans = 1e50;
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> N;
For(i, 1, N) cin >> a[i].x;
For(i, 1, N) cin >> a[i].y;
For(i, 1, N - 1) e[i] = make_line(a[i], a[i + 1]);
sort(e + 1, e + N);
now = 1;
For(i, 2, N - 1) if (e[i].k > e[now].k) e[++now] = e[i];
For(i, 1, now) {
while (tp > 1 && (e[i].b - e[stk[tp - 1]].b) * (-e[stk[tp]].k + e[stk[tp - 1]].k) <
(e[stk[tp]].b - e[stk[tp - 1]].b) * (-e[i].k + e[stk[tp - 1]].k))
tp--;
stk[++tp] = i;
}
For(i, 1, tp - 1) {
c[i] = intersect(e[stk[i]], e[stk[i + 1]]);
if (c[i].x > a[N].x || c[i].x < a[1].x)
continue;
register int w = lower_bound(a + 1, a + N + 1, c[i]) - a;
register line m = make_line(a[w], a[w - 1]);
ans = MIN(ans, c[i].y - m.k * c[i].x - m.b);
}
For(i, 1, N) {
register int w = lower_bound(c + 1, c + tp, a[i]) - c;
ans = MIN(ans, e[stk[w]].k * a[i].x + e[stk[w]].b - a[i].y);
}
cout << fixed << setprecision(3) << ans + eps << endl;//注意精度!
return 0;
}