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 <= A.length = B.length <= 10000
0 <= A[i] <= 10^9
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)