无重叠区间及用最少的箭射爆气球
無重疊區間及用最少的箭射爆氣球
文章目錄
- 無重疊區間及用最少的箭射爆氣球
- **一:開胃菜**
- **二、無重疊區間**
- **三、用最少的箭射爆氣球**
一:開胃菜
在開始所無重疊區間前先做一道簡單的提來鋪墊一下
給你很多形如 [start, end] 的閉區間, 請你設計?個算法,
算出這些區間中最多有?個互不相交的區間
舉個例?, intvs = [[1,3], [2,4], [3,6]] , 這些區間最多有 2 個區間互不相交, 即 [[1,3], [3,6]] , 你的算法應該返回 2。 注意邊界相同并不算相交。
這個問題有許多看起來不錯的貪?思路, 卻都不能得到正確答案。 ?如說:
也許我們可以每次選擇可選區間中開始最早的那個? 但是可能存在某些區間開始很早, 但是很?, 使得我們錯誤地錯過了?些短的區間。
或者我們每次選擇可選區間中最短的那個? 或者選擇出現沖突最少的那個區間? 這些?案都能很容易舉出反例, 不是正確的?案
正確的思路其實很簡單, 可以分為以下三步:
從區間集合 intvs 中選擇?個區間 x, 這個 x 是在當前所有區間中結束最早的(end 最?) 。
把所有與 x 區間相交的區間從區間集合 intvs 中刪除。
重復步驟 1 和 2, 直到 intvs 為空為?。 之前選出的那些 x 就是最?不相交?集
把這個思路實現成算法的話, 可以按每個區間的 end 數值升序排序, 因為這樣處理之后實現步驟 1 和步驟 2 都?便很多:
現在來實現算法, 對于步驟 1, 由于我們預先按照 end 排了序,所以選擇x 是很容易的。 關鍵在于, 如何去除與 x 相交的區間, 選擇下?輪循環的 x呢?
由于我們事先排了序, 不難發現所有與 x 相交的區間必然會與 x 的 end 相交; 如果?個區間不想與 x 的 end 相交, 它的 start 必須要?于(或等于) x 的 end :
直接看代碼:
public int intervalSchedule(int[][] intvs) {if (intvs.length == 0) return 0;// 按 end 升序排序Arrays.sort(intvs, new Comparator<int[]>() {public int compare(int[] a, int[] b) {return a[1] - b[1];}}// 記錄不相交區間的個數,初始化時?少有?個區間不相交int count = 1;// 排序后, 第?個區間就是 xint x_end = intvs[0][1];for (int[] interval : intvs) {int start = interval[0];//代表這個區間和選擇的區間不相交,更新相關變量if (start >= x_end){// 找到下?個選擇的區間了count++;x_end = interval[1];}} return count; }二、無重疊區間
給定一個區間的集合,找到需要移除區間的最小數量,使剩余區間互不重疊。
注意:
可以認為區間的終點總是大于它的起點。 區間 [1,2] 和 [2,3] 的邊界相互“接觸”,但沒有相互重疊。
示例 1:
輸入: [ [1,2], [2,3], [3,4], [1,3] ]輸出: 1解釋: 移除 [1,3] 后,剩下的區間沒有重疊。示例 2:
輸入: [ [1,2], [1,2], [1,2] ]輸出: 2解釋: 你需要移除兩個 [1,2] 來使剩下的區間沒有重疊。示例 3:
輸入: [ [1,2], [2,3] ]輸出: 0解釋: 你不需要移除任何區間,因為它們已經是無重疊的了。我們已經會求最多有?個區間不會重疊了, 那么剩下的不就是?少需要去除的區間嗎?
int eraseOverlapIntervals(int[][] intervals) {int n = intervals.length;return n - intervalSchedule(intervals); }完整C++代碼
class Solution { public:int eraseOverlapIntervals(vector<vector<int>>& intervals) {if (intervals.empty()) return 0;//先對vector進行排序sort(intervals.begin(), intervals.end());//記錄左邊界,[start,end]相當于end位置的元素int left = intervals[0][1];//記錄要移除的個數int res = 0;//循環測試for (int i = 1; i < intervals.size(); ++i) {//如果intervals[i][0] < left(相當于上一個區間的 end 大于循環測試的start)代表兩個區間重復,更新左邊界及計數if (intervals[i][0] < left) {++res;//left在區間重復的情況下,左邊界相當于取上一個區間的end 和 循環測試的某個區間的end的最小值left = min(left, intervals[i][1]);} else {//兩個區間沒有相交,直接判斷下一個區間left = intervals[i][1];}}return res;} };三、用最少的箭射爆氣球
在二維空間中有許多球形的氣球。對于每個氣球,提供的輸入是水平方向上,氣球直徑的開始和結束坐標。由于它是水平的,所以y坐標并不重要,因此只要知道開始和結束的x坐標就足夠了。開始坐標總是小于結束坐標。平面內最多存在104個氣球。
一支弓箭可以沿著x軸從不同點完全垂直地射出。在坐標x處射出一支箭,若有一個氣球的直徑的開始和結束坐標為 xstart,xend, 且滿足 xstart ≤ x ≤ xend,則該氣球會被引爆。可以射出的弓箭的數量沒有限制。 弓箭一旦被射出之后,可以無限地前進。我們想找到使得所有氣球全部被引爆,所需的弓箭的最小數量。
Example:
輸入: [[10,16], [2,8], [1,6], [7,12]]輸出: 2解釋: 對于該樣例,我們可以在x = 6(射爆[2,8],[1,6]兩個氣球)和 x = 11 (射爆另外兩個氣球)。其實稍微思考?下, 這個問題和區間調度算法?模?樣,只不過是氣球變成了區間而已! 如果最多有 n 個不重疊的區間, 那么就?少需要 n 個箭頭穿透所有區間:
只是有?點不?樣, 在 intervalSchedule 算法中, 如果兩個區間的邊界觸碰, 不算重疊; ?按照這道題?的描述(開始和結束坐標為 xstart,xend, 且滿足 xstart ≤ x ≤ xend,則該氣球會被引爆), 箭頭如果碰到?球的邊界?球也會爆炸, 所以說相當于區間的邊界觸碰也算重疊:
所以只要將之前的算法稍作修改, 就是這道題?的答案:
int findMinArrowShots(int[][] intvs) { //for (int[] interval : intvs) {int start = interval[0];// 把 >= 改成 > 就?了if (start > x_end) {count++;x_end = interval[1];}} return count; }完整C++代碼
class Solution { public:int findMinArrowShots(vector<vector<int>>& points) {if(points.empty()){return 0;}//排序sort(points.begin(),points.end());//定義第一個區間的結束位置為end,后序區間都會和end進行比較int end = points[0][1];//用來計數int count = 1;//循環遍歷所有區間for(int i = 1;i < points.size();i++){//定義每個區間的開始位置為startint start = points[i][0];//如果start > end 說明兩個區間沒有重疊,更新相關變量if(start > end){++count;end = points[i][1];}else{//取當前end和重疊區間的end的較小值end = min(end,points[i][1]);}}return count;} };總結
以上是生活随笔為你收集整理的无重叠区间及用最少的箭射爆气球的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Nim 游戏 、⽯头游戏1、石头游戏2
- 下一篇: 动态规划之KMP字符匹配算法