[LeetCode] Set Intersection Size At Least Two 設置交集大小至少為2


 

An integer interval [a, b] (for integers a < b) is a set of all consecutive integers from a to b, including a and b.

Find the minimum size of a set S such that for every integer interval A in intervals, the intersection of S with A has size at least 2.

Example 1:

Input: intervals = [[1, 3], [1, 4], [2, 5], [3, 5]]
Output: 3
Explanation:
Consider the set S = {2, 3, 4}.  For each interval, there are at least 2 elements from S in the interval.
Also, there isn't a smaller size set that fulfills the above condition.
Thus, we output the size of this set, which is 3.

 

Example 2:

Input: intervals = [[1, 2], [2, 3], [2, 4], [4, 5]]
Output: 5
Explanation:
An example of a minimum sized set is {1, 2, 3, 4, 5}.

 

Note:

  1. intervals will have length in range [1, 3000].
  2. intervals[i] will have length 2, representing some integer interval.
  3. intervals[i][j] will be an integer in [0, 10^8].

 

這道題給了我們一些區間,讓我們求一個集合S,使得S和每個區間的交集至少為2,即至少有兩個相同的數字。博主最開始分析題目中的例子的時候,以為要求的集合S也必須是一個連續的區間,其實不需要的,離散的數字就可以了。比如如果區間是[1,3], [5,6]的話,那么返回的集合長度是4,而不是5。這道題可以是用貪婪算法來解,一般來說Hard的題目能用貪婪算法而不是DP解的是少之又少,這道題為我大Greedy算法正名了~!為了使得集合S中的數字盡可能的小,我們希望處理區間的時候從小區間開始,如果區間b完全覆蓋了區間a,那么和區間a有兩個相同數字的集合,一定和區間b也有兩個相同數字。同樣,我們不希望一會處理一個前面的區間,一會又處理一個很后面的區間,我們希望區間是有序的。那么如何給區間排序呢,是按起始位置排,還是按結束位置排,這里我們按結束位置從小往大排,當兩個結束位置相同時,起始位置大的排前面先處理,這也符合我們先處理小區間的原則。那么遍歷區間的時候,當前區間就和我們維護的集合S有三種情況:

1. 二者完全沒有交集,這時候我們就需要從當前區間中取出兩個數字加入集合S,取哪兩個數呢?為了盡可能少使用數字,我們取當前區間中的最大兩個數字,因為我們區間位置不斷變大,所以取大的數字有更高的概率能和后面的區間有交集。

2. 二者有一個數字的交集,那么這個交集數字一定是區間的起始位置,那么我們需要從當前區間中再取一個數字加入集合S,根據上面的分析,我們取最大的那個數,即區間的結束位置。

3. 二者有兩個及兩個以上數字的交集,那么不用做任何處理。

好,分析到這里,代碼也就不難寫出來了,我們用個數組v來表示集合S,初始化放兩個-1進去,因為題目中說明了區間都是大於0的,所以我們放這兩個數組進去是為了防止越界的,不會有任何影響,最后統計長度的時候減去這個兩個數字就可以了。先給區間排序,然后遍歷每個區間,如果區間的起始位置小於等於數組的倒數第二個數字,說明此時已經有兩個相同的數字了,直接跳過當前區間。否則如果區間的起始位置大於數組的最后一個位置,說明二者沒有任何交集,我們此時先把區間的倒數第二小的數字加入數組v中。然后統一再把區間的結束位置加入數組v中,因為不管區間和數組有一個交集還是沒有任何交集,結束位置都要加入數組中,所以不用放在if..else..中。最后循環結束,返回數組的大小減2,參見代碼如下:

 

解法一:

class Solution {
public:
    int intersectionSizeTwo(vector<vector<int>>& intervals) {
        vector<int> v{-1, -1};
        sort(intervals.begin(), intervals.end(), [](vector<int>& a, vector<int>& b){
            return a[1] < b[1] || (a[1] == b[1] && a[0] > b[0]);
        });
        for (auto &interval : intervals) {
            int len = v.size();
            if (interval[0] <= v[len - 2]) continue;
            if (interval[0] > v.back()) v.push_back(interval[1] - 1);
            v.push_back(interval[1]);
        }
        return v.size() - 2;
    }
};

 

我們可以對空間復雜度進行優化,我們不用保存整個集合S,因為結果只讓我們返回長度即可,所以我們用兩個變量p1和p2,其中p1表示集合S中倒數第二大的數字,p2為集合S中最大的數字。我們的整個邏輯跟上面的解法是相同的。遍歷區間的時候,如果區間的起始位置小於等於p1,則跳過當前區間。否則如果起始位置大於p2,說明沒有交集,需要加上兩個數字,結果res自增2,然后p2賦值為當前區間的結束位置,p1賦值為第二大的數字。否則說明只有一個交集,結果res自增1,然后p1賦值為p2,p2賦值為當前區間的結束位置即可,參見代碼如下:

 

解法二:

class Solution {
public:
    int intersectionSizeTwo(vector<vector<int>>& intervals) {
        int res = 0, p1 = -1, p2 = -1;
        sort(intervals.begin(), intervals.end(), [](vector<int>& a, vector<int>& b){
            return a[1] < b[1] || (a[1] == b[1] && a[0] > b[0]);
        });
        for (auto &interval : intervals) {
            if (interval[0] <= p1) continue;
            if (interval[0] > p2) {
                res += 2;
                p2 = interval[1];
                p1 = p2 - 1;
            } else {
                ++res;
                p1 = p2;
                p2 = interval[1];
            }
        }
        return res;
    }
};

 

討論:這道題的一個拓展就是一般化,改為交集大小至少為k,那么我們該怎么做呢?其實也不是很難,我們可以在解法一的基礎上進行修改,我們還是用數組v來表示集合S,只不過我們初始化加入k個-1。然后還是要給區間排序,之后進行遍歷,如果起始位置小於等於倒數第k個數,跳過當前區間。然后就是重點部分了,我們還是用起始位置跟數組v的最后面的數字比較,總共比到倒數第k-1個數就行了,因為小於等於倒數第k個數已經跳過了。如果大於最后一個數,則將區間后k個數加入數組;否則如果大於倒數第二個數,則將區間后k-1個數加入數組;否則如果大於倒數第三個數,則將數組后k-2個數加入數組,以此類推,直到比完倒數第k-1個數就行了,最后返回的是數組v的長度減去k。

 

參考資料:

https://leetcode.com/problems/set-intersection-size-at-least-two/discuss/117545/C++-sort-and-greedy-10-lines

https://leetcode.com/problems/set-intersection-size-at-least-two/discuss/113089/C++-concise-solution-O(nlogn)-greedy-39-ms

https://leetcode.com/problems/set-intersection-size-at-least-two/discuss/113085/Ever-wonder-why-the-greedy-algorithm-works-Here-is-the-explanation!

 

LeetCode All in One 題目講解匯總(持續更新中...)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM