题解:P7401 [COCI2020-2021#5] Planine
题意
现有一座上下起伏的山。它可以抽象为一个包含
( 为奇数)个点 以及 与 的多边形。 对于所有满足
, , 的整数 , 都是山谷。 现要放置若干个高度为
的点光源,使得所有的山谷都被照亮,即点光源与山谷的连线不经过山的内部。 求所需点光源的最少数量。
分析
考虑对每个山谷处理出能照亮它的点集。
比如对于如下山谷:
处理出第
对于第
但是第
我们发现它还受另外的点的限制。
考虑维护一个凸包,如下图:
点集所受的限制就是凸包中最后一条边。
所以我们正向扫一遍,维护一个凸包,再反向扫一遍,维护另一个凸包即可。
时间复杂度
现在我们得到了若干个区间,我们需要选取若干个点,使得每个区间内至少存在一个点,并最小化点的个数。
这是一个比较经典的贪心问题。
考虑按右端点从小到大排序,每次选取右端点最小的区间,并去除所有与该区间相交的区间。
答案即为选取的次数。
正确性显然。
Code
#include<bits/stdc++.h> using namespace std; #define maxn 1000006 typedef long double f64_t; struct dot{int x, y;}ds[maxn]; f64_t slope(dot a, dot b) {return (f64_t)(a.y-b.y)/(a.x-b.x);} struct segement { f64_t l, r; segement(f64_t x=0, f64_t y=0): l(x), r(y) {} friend bool operator<(segement a, segement b) {return a.r<b.r; } }seg[maxn]; vector<int> s; #define chk(x) ((x)!=1&&(x)!=n&&((x)&1)) f64_t back() {return slope(ds[*s.rbegin()], ds[*(s.rbegin()+1)]);} int main() { int n, h; cin>>n>>h; for(int i=1;i<=n;i++) cin>>ds[i].x>>ds[i].y; for(int i=1;i<=n;i++) { while(s.size()>1&&back()<=slope(ds[s.back()], ds[i])) s.pop_back(); s.emplace_back(i); if(chk(i)) { f64_t k=back(); f64_t b=ds[i].y-k*ds[i].x; seg[i].l=(h-b)/k; } } s.clear(); for(int i=n;i;i--) { while(s.size()>1&&back()>=slope(ds[s.back()], ds[i])) s.pop_back(); s.emplace_back(i); if(chk(i)) { f64_t k=back(); f64_t b=ds[i].y-k*ds[i].x; seg[i].r=(h-b)/k; } } vector<segement> vc; for(int i=3;i<n;i+=2) vc.emplace_back(seg[i]); sort(vc.begin(), vc.end()); f64_t mxr=-1e18; int ans=0; for(auto [l, r]:vc) if(l-1e-8>mxr) mxr=r, ans++; cout<<ans; }
本文作者:Jimmy-LEEE
本文链接:https://www.cnblogs.com/redacted-area/p/18379564
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步