Suppose LeetCode will start its IPO soon. In order to sell a good price of its shares to Venture Capital, LeetCode would like to work on some projects to increase its capital before the IPO. Since it has limited resources, it can only finish at most k distinct projects before the IPO. Help LeetCode design the best way to maximize its total capital after finishing at most k distinct projects.
You are given several projects. For each project i, it has a pure profit Pi and a minimum capital of Ci is needed to start the corresponding project. Initially, you have W capital. When you finish a project, you will obtain its pure profit and the profit will be added to your total capital.
To sum up, pick a list of at most k distinct projects from given projects to maximize your final capital, and output your final maximized capital.
Example 1:
Input: k=2, W=0, Profits=[1,2,3], Capital=[0,1,1].
Output: 4
Explanation: Since your initial capital is 0, you can only start the project indexed 0.
After finishing it you will obtain profit 1 and your capital becomes 1.
With capital 1, you can either start the project indexed 1 or the project indexed 2.
Since you can choose at most 2 projects, you need to finish the project indexed 2 to get the maximum capital.
Therefore, output the final maximized capital, which is 0 + 1 + 3 = 4.
Note:
- You may assume all numbers in the input are non-negative integers.
- The length of Profits array and Capital array will not exceed 50,000.
- The answer is guaranteed to fit in a 32-bit signed integer.
這道題上來就讓人眼前一亮,劍指上市,每個創業公司都有一個上市的夢想吧,博主認為照現在這種發展趨勢,感覺上市並非遙不可及,資瓷一下。這道題說初始時我們的資本為0,可以交易k次,並且給了我們提供了交易所需的資本和所能獲得的利潤,讓我們求怎樣選擇k次交易,使我們最終的資本最大。雖然題目中給我們的資本數組是有序的,但是OJ里的test case肯定不都是有序的,還有就是不一定需要資本大的交易利潤就多,該遍歷的時候還得遍歷。我們可以用貪婪算法來解,每一次都選擇資本范圍內最大利潤的進行交易,那么我們首先應該建立資本和利潤對,然后根據資本的大小進行排序,然后我們根據自己當前的資本,用二分搜索法在有序數組中找第一個大於當前資本的交易的位置,然后往前退一步就是最后一個不大於當前資本的交易,然后向前遍歷,找到利潤最大的那個的進行交易,把利潤加入資本W中,然后將這個交易對刪除,這樣我們就可以保證在進行k次交易后,我們的總資本最大,參見代碼如下:
解法一:
class Solution { public: int findMaximizedCapital(int k, int W, vector<int>& Profits, vector<int>& Capital) { vector<pair<int, int>> v; for (int i = 0; i < Capital.size(); ++i) { v.push_back({Capital[i], Profits[i]}); } sort(v.begin(), v.end()); for (int i = 0; i < k; ++i) { int left = 0, right = v.size(), mx = 0, idx = 0; while (left < right) { int mid = left + (right - left) / 2; if (v[mid].first <= W) left = mid + 1; else right = mid; } for (int j = right - 1; j >= 0; --j) { if (mx < v[j].second) { mx = v[j].second; idx = j; } } W += mx; v.erase(v.begin() + idx); } return W; } };
看論壇上的大神們都比較喜歡用一些可以自動排序的數據結構來做,比如我們可以使用一個最大堆和一個最小堆,把資本利潤對放在最小堆中,這樣需要資本小的交易就在隊首,然后從隊首按順序取出資本小的交易,如果所需資本不大於當前所擁有的資本,那么就把利潤資本存入最大堆中,注意這里資本和利潤要翻個,因為我們希望把利潤最大的交易放在隊首,便於取出,這樣也能實現我們的目的,參見代碼如下:
解法二:
class Solution { public: int findMaximizedCapital(int k, int W, vector<int>& Profits, vector<int>& Capital) { priority_queue<pair<int, int>> maxH; priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> minH; for (int i = 0; i < Capital.size(); ++i) { minH.push({Capital[i], Profits[i]}); } for (int i = 0; i < k; ++i) { while (!minH.empty() && minH.top().first <= W) { auto t = minH.top(); minH.pop(); maxH.push({t.second, t.first}); } if (maxH.empty()) break; W += maxH.top().first; maxH.pop(); } return W; } };
下面這種方法跟上面的解法思路完全一樣,就是數據結構有些變化,我們用multiset來模擬最小堆,然后最大堆還是用優先隊列來實現,不過是需要存利潤值就行了,不需要存對應的資本了,參見代碼如下:
解法三:
class Solution { public: int findMaximizedCapital(int k, int W, vector<int>& Profits, vector<int>& Capital) { priority_queue<int> q; multiset<pair<int, int>> s; for (int i = 0; i < Capital.size(); ++i) { s.insert({Capital[i], Profits[i]}); } for (int i = 0; i < k; ++i) { for (auto it = s.begin(); it != s.end(); ++it) { if (it->first > W) break; q.push(it->second); s.erase(it); } if (q.empty()) break; W += q.top(); q.pop(); } return W; } };
參考資料:
https://discuss.leetcode.com/topic/77768/very-simple-greedy-java-solution-using-two-priorityqueues
