1. 題目描述
【翻譯過來】:
有若干個氣球,給出了它們的直徑兩個端點的座標(X軸),我們的工作是需要用一支箭來戳破氣球。例如兩個氣球的直徑分別是(1,6)和(2,8),那麼我們就可以在x=6的座標處發射一支箭從而將它們一起戳破。求解的問題是:給出一系列氣球直徑的座標之後,最少能用多少支箭就可以戳破它們。
2. 樣例
Input:
[[10,16], [2,8], [1,6], [7,12]]
Output:
2
Explanation:
One way is to shoot one arrow for example at x = 6 (bursting the balloons [2,8] and [1,6]) and another arrow at x = 11 (bursting the other two balloons).
3. 分析
題目的意思很明確,就是利用最少的箭來戳破氣球。分析一下,我們不難得到這樣的結論:我們對所有的氣球所在的位置進行整理,如果發現兩個氣球有重疊的部分,就說明它們有可能被同一支箭戳破。
有了這樣的一個概念,接下來就是具體做法:我們將所有的氣球按照它們的直徑左端點進行升序排列,這樣我們得到的序列就有了很好的性質:後一個氣球很有可能與前一個氣球重合。
接下來,我們可以用這樣的方法:我們維護一個reference參考區間,這個區間代表的是目前爲止最小的氣球重合部分,即如果發射箭的話,射箭的座標就應該在這個reference區間裏面,如果接下來的氣球左端點都不在這個區間裏面,那麼它就不會和之前的若干個氣球一起被射中了,就要用新的箭。
即:這個貪心算法的思想就是:目前reference參考區間維護的是到目前爲止能夠被一起射中的氣球的重疊部分。
進行遍歷的時候:判斷當前氣球的左右座標與reference區間關係:
- 如果該氣球在區間內部,說明該氣球可以和之前的氣球一起被射中,區間需要縮小至該氣球的左右端點;
- 如果左端點在區間內部,右端點不在,說明該氣球有一部分和區間重合,所以區間需要縮小:右端點不變,左端點爲該氣球的左端點;
- 如果左端點不在區間內部,說明該氣球與之前的氣球不能一起被射中,需要新增一支箭,之前的氣球都被戳破,reference變爲該氣球區間重新探尋;
4. 源碼
class Solution {
public:
static bool Compare(pair<int, int>point1, pair<int, int>point2) {
if (point1.first <= point2.first) {
if (point1.first == point2.first) {
return point1.second < point2.second;
}
return true;
}
return false;
}
int findMinArrowShots(vector<pair<int, int>>& points) {
int counter = 1;
sort(points.begin(), points.end(), Compare);
pair<int, int>reference;
if (points.size() != 0) {
reference = points[0];
}
for (int i = 0; i < (int)points.size(); i++) {
if (points[i].first <= reference.second) {
if (points[i].second <= reference.second) {
reference.first = points[i].first;
reference.second = points[i].second;
}
else {
reference.first = points[i].first;
}
}
else {
counter++;
reference = points[i];
}
}
if (points.size() == 0) {
return 0;
}
return counter;
}
};
5. 心得
這道題,我自己設計的解法居然能夠打敗98.95%的人,有點小開心。儘管剛開始走了一點彎路:認爲只要每一個氣球與前一個氣球有重疊部分就可以一起射中,這個錯誤的思路導致了我提交了3次的Wrong Answer,因爲只與前一個氣球重疊,不代表和再之前的能夠重疊。