團體程序設計天梯賽練習集 PAT-L3-009 長城 凸包

題目連接: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; }
相關文章
相關標籤/搜索