Task Planine
题意
给一座山脉的二维坐标系模型,以轮廓的坐标点来表示,求最少几个光源可以覆盖每一个山谷。
思路
有一些前置知识是,计算几何的基本函数编写,对凸包有一定的理解。
容易想到把所有的谷映射到h高度的一些区间,对区间做贪心的算法。
这里就是我用一个segs
存所有区间的边界点,tag
标志左右,id
表示第几个区间,x
表示位置。
int dcmp(double x) {
if (fabs(x) < EPS) return 0;
return x < 0 ? -1 : 1;
}
struct Seg {
double x;
int tag; // l:0, r:1
int id;
bool operator<(const Seg &r) const {
if (dcmp(x - r.x) == 0) return tag < r.tag;
return dcmp(x - r.x) < 0;
}
};
Seg segs[MAXN];
int vis[MAXN]; // 0: unvis, 1: wip
int stk[MAXN], stk_top;
贪心的思路就是,每次枚举到右边界,就清算之前所有的左边界,记一次答案,并且标记为已经点亮。
{
int ans = 0;
for (int i = 1; i <= segs_cnt; ++i) {
const Seg &s = segs[i];
if (s.tag == 0) {
stk[++stk_top] = s.id;
} else {
if (vis[s.id]) continue;
ans++;
while (stk_top) {
vis[stk[stk_top--]] = 1;
}
}
}
}
下面的问题就是如何投射这些区间。
通过观察一些比较极端的样例,可以发现,所有的区间夹角都在相邻的凸包边上。随着上凸包的不断更新,在对应的点就可以找到边界无遮挡的射线。
下面的唯一的难点就在于一边计算上图包,一边计算每个谷投射区间边界的射线。
{
hull[1] = p[1];
hull[2] = p[2];
hull_cnt = 2;
int segs_cnt = 0;
for (int j = 3; j < n; j++) {
while (hull_cnt > 2 && Cross(hull[hull_cnt] - hull[hull_cnt - 1], p[j] - hull[hull_cnt - 1]) > 0)
hull_cnt--;
hull[++hull_cnt] = p[j];
if (1 < j && j < n && j % 2 == 1) {
double x = getx(hull[hull_cnt - 1], hull[hull_cnt], h);
segs[++segs_cnt] = {x, 0, j / 2};
}
}
}
对凸包有理解话,实际上和凸包有关的核心代码只有那个while,我使用的是Graham扫描的算法原理。这个while在不断的维护顺时针的上凸包。这片代码在插入所有谷投射的左边界。
下一个for是在求右边界,但是因为顺序是反着的,代码顺序有一些细节,先学过凸包可以慢慢推理。
完整代码
/**
* █████╗ ██████╗ ██████╗ ██╗ ███████╗
* ██╔══██╗██╔════╝ ██╔══██╗██║ ╚══███╔╝
* ███████║██║ ██████╔╝██║ ███╔╝
* ██╔══██║██║ ██╔═══╝ ██║ ███╔╝
* ██║ ██║╚██████╗▄█╗ ██║ ███████╗███████╗
* ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝ ╚══════╝╚══════╝
*
* @Author: TieWay59
* @Created: 2021/3/20 19:48
* @Link: http://10.7.95.2/CLanguage/contests/1181/problems/1010.html
* @Tags:
*
*******************************************************/
#include <bits/stdc++.h>
#ifdef DEBUG
# include "libs59/debugers.h"
// #define debug(x) cerr <<#x << " = "<<x<<endl;
#else
# define endl '\n'
# define debug(...)
# define max(x,y) ((x)>(y)?(x):(y))
# define min(x,y) ((x)>(y)?(y):(x))
#endif
#define STOPSYNC ios::sync_with_stdio(false);cin.tie(nullptr)
#define MULTIKASE int Kase=0;cin>>Kase;for(int kase=1;kase<=Kase;kase++)
typedef long long ll;
const int MAXN = 1e6 + 59;
const int MOD = 1e9 + 7;
const int INF = 0x3F3F3F3F;
const ll llINF = 0x3F3F3F3F3F3F3F3F;
using namespace std;
using pii = pair<int, int>;
using vint = vector<int>;
const double EPS = 1e-8;
int dcmp(double x) {
if (fabs(x) < EPS) return 0;
return x < 0 ? -1 : 1;
}
struct Point {
double x, y;
};
Point p[MAXN];
Point hull[MAXN];
int hull_cnt;
inline double Cross(Point a, Point b) {
return a.x * b.y - a.y * b.x;
}
inline Point operator-(Point a, Point b) {
return Point{a.x - b.x, a.y - b.y};
}
double getx(const Point &a, const Point &b, double h) {
return (h - a.y) / (b.y - a.y) * (b.x - a.x) + a.x;
}
struct Seg {
double x;
int tag; // l:0, r:1
int id;
bool operator<(const Seg &r) const {
if (dcmp(x - r.x) == 0) return tag < r.tag;
return dcmp(x - r.x) < 0;
}
};
Seg segs[MAXN];
int vis[MAXN]; // 0: unvis, 1: wip
int stk[MAXN], stk_top;
void solve(int kaseId = -1) {
int n, h; // n is odd
cin >> n >> h;
for (int i = 1, x, y; i <= n; ++i) {
cin >> x >> y;
p[i] = {double(x), double(y)};
}
if (n == 3) {
cout << 0 << endl;
return;
}
hull[1] = p[1];
hull[2] = p[2];
hull_cnt = 2;
int segs_cnt = 0;
for (int j = 3; j < n; j++) {
while (hull_cnt > 2 && Cross(hull[hull_cnt] - hull[hull_cnt - 1], p[j] - hull[hull_cnt - 1]) > 0)
hull_cnt--;
hull[++hull_cnt] = p[j];
if (1 < j && j < n && j % 2 == 1) {
double x = getx(hull[hull_cnt - 1], hull[hull_cnt], h);
segs[++segs_cnt] = {x, 0, j / 2};
}
}
hull[1] = p[n];
hull[2] = p[n - 1];
hull_cnt = 2;
for (int j = n - 2; j > 1; j--) {
while (hull_cnt > 2 && Cross(hull[hull_cnt - 1] - p[j], hull[hull_cnt - 1] - hull[hull_cnt]) > 0)
hull_cnt--;
hull[++hull_cnt] = p[j];
if (1 < j && j < n && j % 2 == 1) {
double x = getx(hull[hull_cnt - 1], hull[hull_cnt], h);
segs[++segs_cnt] = {x, 1, j / 2};
}
}
sort(segs + 1, segs + 1 + segs_cnt);
int ans = 0;
for (int i = 1; i <= segs_cnt; ++i) {
const Seg &s = segs[i];
if (s.tag == 0) {
stk[++stk_top] = s.id;
} else {
if (vis[s.id]) continue;
ans++;
while (stk_top) {
vis[stk[stk_top--]] = 1;
}
}
}
cout << ans << endl;
}
void solves() {
MULTIKASE {
solve(kase);
}
}
int main() {
#ifdef DEBUG
freopen("input.txt", "r+", stdin);
#endif
STOPSYNC;
solve();
return 0;
}
/*
*/