题目连接:https://www.patest.cn/contests/gplt/L3-009测试
这道题拖了很久才AC掉。以前想的好几种贪心思路都是错的,不过也能拿26分。(测试点分值的分布好诡异……)spa
错误的贪心思路之一:从右往左处理时,只考虑上一个监视点。能够构造出这样一个反例:code
蓝色的点不能被左边的监视点覆盖,但能够被右边的监视点覆盖。blog
本题的正解是:维护一个上凸的半凸包:string
截止到绿色节点时,凸包的形状如黄色虚线所示,两个黄色节点是监视点。it
而后咱们继续向左处理,当前节点(蓝色)能够直接加入凸包,而不须要移除其中任何节点。所以不难发现,现有的两个监视点不能覆盖当前位置,咱们必须将绿色节点也设为监视点。io
继续向左,将当前节点加入凸包时须要移除其中两个节点,以下图:class
由图可知当前节点能够被监视。test
继续这个过程:im
最后一张图时,将当前节点加入凸包不须要移除其中任何节点,说明当前位置没法受监视,须要将绿色节点也设为监视点。
综上可知,在维护上凸的半凸包时,若是加入某个节点不会致使凸包里其余节点被移除,那么以前一个节点就应该被设为监视点,答案+1。
代码以下:
#include <cstdio> #include <cstring> #include <algorithm> #include <vector> using namespace std; struct Point { int x, y; }; vector<Point> stk; int ans = 0; int N; long long multi(int x1, int y1, int x2, int y2) { return 1LL * x1 * y2 - 1LL * x2 * y1; } int main() { int x, y, lx, ly; scanf("%d", &N); scanf("%d%d%d%d", &x, &y, &lx, &ly); stk.push_back({x, y}); stk.push_back({lx, ly}); for (int i = 3; i <= N; i++) { scanf("%d%d", &x, &y); bool ok = false; while (stk.size() >= 2) { Point &p1 = stk.back(); Point &p2 = stk[stk.size() - 2]; if (multi(p1.x - p2.x, p1.y - p2.y, x - p1.x, y - p1.y) <= 0) { ok = true; stk.pop_back(); } else break; } if (!ok) ans += 1; stk.push_back({x, y}); } printf("%d", ans); return 0; }