[LeetCode] 870. Advantage Shuffle 優勢洗牌



Given two arrays `A` and `B` of equal size, the *advantage of `A` with respect to `B`* is the number of indices `i` for which `A[i] > B[i]`.

Return any permutation of A that maximizes its advantage with respect to B.

Example 1:

Input: A = [2,7,11,15], B = [1,10,4,11]
Output: [2,11,7,15]

Example 2:

Input: A = [12,24,8,32], B = [13,25,32,11]
Output: [24,32,8,12]

Note:

  1. 1 <= A.length = B.length <= 10000
  2. 0 <= A[i] <= 10^9
  3. 0 <= B[i] <= 10^9

這道題給了我們兩個數組A和B,讓對A進行重排序,使得每個對應對位置上A中的數字盡可能的大於B。這不就是大名鼎鼎的田忌賽馬么,但想出高招並不是田忌,而是孫臏,就是孫子兵法的作者,但這 credit 好像都給了田忌,讓人誤以為是田忌的智慧,不禁想起了高富帥重金買科研成果的冠名權的故事。孫子原話是,“今以君之下駟與彼上駟,取君上駟與彼中駟,取君中駟與彼下駟”。就是自己的下馬跟人上馬比,穩輸不用管,上馬跟其中馬跑,穩贏,中馬跟其下馬跑,還是穩贏。那我還全馬跟其半馬跑,能贏否?不過說的,今天博主所在的城市還真有馬拉松比賽,而且博主還報了半馬,但是由於身不由己的原因無法去跑,實在是可惜,沒事,來日方長,總是有機會的。扯了這么久的犢子,趕緊拉回來做題吧。其實這道題的思路還真是田忌賽馬的智慧一樣,既然要想辦法大過B中的數,那么對於B中的每個數(可以看作每匹馬),先在A中找剛好大於該數的數字(這就是為啥中馬跟其下馬比,而不是上馬跟其下馬比),用太大的數字就浪費了,而如果A中沒有比之大的數字,就用A中最小的數字(用下馬跟其上馬比,不過略有不同的是此時我們沒有上馬)。就用這種貪婪算法的思路就可以成功解題了,為了方便起見,就是用一個 MultiSet 來做,相當於一個允許重復的 TreeSet,既允許重復又自帶排序功能,豈不美哉!那么遍歷B中每個數字,在A進行二分搜索第一個大於的數字,這里使用了 STL 自帶的 upper_bound 來做,當然想自己寫二分也沒問題。然后看,若不存在,則將A中最小的數字加到結果 res 中,否則就將第一個大於的數字加入結果 res 中,參見代碼如下:
解法一:
class Solution {
public:
    vector<int> advantageCount(vector<int>& A, vector<int>& B) {
		vector<int> res;
		multiset<int> st(A.begin(), A.end());
		for (int i = 0; i < B.size(); ++i) {
			auto it = (*st.rbegin() <= B[i]) ? st.begin() : st.upper_bound(B[i]);
			res.push_back(*it);
			st.erase(it);
		}
		return res;
	}
};

當兩個數組都是有序的時候,我們就能快速的直到各自的最大值與最小值,問題就變得容易很多了。比如可以先從B的最大值開始,這是就看A的最大值能否大過B,能的話,就移動到對應位置,不能的話就用最小值,然后再看B的次大值,這樣雙指針就可以解決問題。所以可以先給A按從小到大的順序,對於B的話,不能直接排序,因為這樣的話原來的順序就完全丟失了,所以將B中每個數字和其原始坐標位置組成一個 pair 對兒,加入到一個最大堆中,這樣B中的最大值就會最先被取出來,再進行上述的操作,這時候就可以發現保存的原始坐標就發揮用處了,根據其坐標就可以直接更新結果 res 中對應的位置了,參見代碼如下:
解法二:
class Solution {
public:
    vector<int> advantageCount(vector<int>& A, vector<int>& B) {
		int n = A.size(), left = 0, right = n - 1;
		vector<int> res(n);
		sort(A.begin(), A.end());
		priority_queue<pair<int, int>> q;
		for (int i = 0; i < n; ++i) q.push({B[i], i});
		while (!q.empty()) {
			int val = q.top().first, idx = q.top().second; q.pop();
			if (A[right] > val) res[idx] = A[right--];
			else res[idx] = A[left++];
		}
		return res;
	}
};

Github 同步地址:

https://github.com/grandyang/leetcode/issues/870


參考資料:

https://leetcode.com/problems/advantage-shuffle/

https://leetcode.com/problems/advantage-shuffle/discuss/149831/C%2B%2B-6-lines-greedy-O(n-log-n)

https://leetcode.com/problems/advantage-shuffle/discuss/149822/JAVA-Greedy-6-lines-with-Explanation


[LeetCode All in One 題目講解匯總(持續更新中...)](https://www.cnblogs.com/grandyang/p/4606334.html)


免責聲明!

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



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