切換
舊版
前往
大廳
主題

ZeroJudge - c807: 一維凸包問題 解題心得

Not In My Back Yard | 2019-01-01 15:50:50 | 巴幣 2 | 人氣 388

題目連結:


題目大意:
「一維凸包」,意指找到一條最短的線段,使得點集合 S 中每一點(一維空間中的點)都在此線段上。

現在給定一正整數 N ( 1 ≦ N ≦ 1, 000, 000),代表接下來有 N 列的指令。

每列指令的開頭有一數字,可能是 1 或是 2 。若為 1 ,則接著會有一正整數 x ,代表把座標為 (x) 的點插入集合之中;若為 2,則接下來會有兩正整數 l 、 r ,代表要把座標介於 l ~ r 之間(包含)的點刪除。

在每次操作後,輸出此點集合S 的一維凸包之頂點數及頂點座標(由小到大排列)。若此集合中無任何點,則輸出「0」。

註:以上的 |x| 、 |l| 、 |r| 皆 ≦ 10 ^ 9 。



範例輸入:
4
1 10
1 20
1 15
2 10 20


範例輸出:
1 10
2 10 20
2 10 20
0


解題思維:
因為這題的關係,所以今天來介紹一下 C++ 的 STL (Standard Template Library,標準模板庫)之中有一個特殊的容器 —— set(集合)。

跟一般數學定義一樣, C++ 的 set 是用來存取相異的同質(Homogeneous)元素。像是數字的集合 { 1, 2, 3 }或是字串的集合 { "Wee",  "yee", "bee" } 等等。

因為以上特性,使得 C++ 內建的 set 非常適合用來解這題(至少不用自己刻一個 set XD)。

而 C++ 之 set 除了跟其他容器(Container)一樣有自己的迭代器(Iterator),也支援 insert(元素)(插入元素到集合裡)、lower_bound(參數)(用來找第一個大於等於參數的元素之位置(回傳一個迭代器的值))、erase(迭代器1, 迭代器2)(用來移除迭代器1(含) ~ 迭代器2(不含)之間的元素)以及回傳集合元素數量的 size()

而這題剛好就只需這四個函式。以下是程式大致上的運行模式:
宣告一集合 P ,並宣告兩個集合的迭代器 first、second。

如果指令為:「1 x」,代表要插入 x 到 P 中。呼叫 P.insert(x) 即可。

如果指令為:「2 l r」,代表要移除值介於 l ~ r 之間(含)的元素。我們可以先令 first =P.lower_bound(l)second = P.lower_bound(r) ,這樣子 first 就是存第一個大於等於 l 的元素位置、second 則是存第一個大於等於 r 元素位置,就找到了我們要移除的區間 [l, r]。

而因為 erase(first, second) 函式不會移除 second 指到的元素。因此如果剛好 second 指到的元素等於 r 的值,就不會被移除到,於是無法達成所需。所以要把 second 指向下一個元素(如果有下一個元素的話)。接著就可以安心地呼叫函式 erase(first, second)

最後,在每個指令結束後,使用 P.size() 的值判斷一下集合的大小。如果大於等於 2 ,就輸出值最小和最大的兩個元素;如果大小為 1 ,就輸出裡面唯一一個的元素;再不然,輸出「0」。

然後記得使用 cin.sync_with_stdio(false)cin.tie(0) 最佳化輸出入的速度。

此次分享到此為止,如有任何更加簡潔的想法或是有說明不清楚之地方,也煩請各位大大撥冗討論。

創作回應

更多創作