《LeetCode之每日一题》:143.IPO
Posted 是七喜呀!
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《LeetCode之每日一题》:143.IPO相关的知识,希望对你有一定的参考价值。
有关题目
假设 力扣(LeetCode)即将开始 IPO(首次公开募股) 。
为了以更高的价格将股票卖给风险投资公司,
力扣 希望在 IPO 之前开展一些项目以增加其资本。
由于资源有限,它只能在 IPO 之前完成最多 k 个不同的项目。
帮助 力扣 设计完成最多 k 个不同项目后得到最大总资本的方式。
给你 n 个项目。
对于每个项目 i ,它都有一个纯利润 profits[i] ,
和启动该项目需要的最小资本 capital[i] 。
最初,你的资本为 w 。
当你完成一个项目时,你将获得纯利润,且利润将被添加到你的总资本中。
总而言之,从给定项目中选择 最多 k 个不同项目的列表,
以 最大化最终资本 ,并输出最终可获得的最多资本。
答案保证在 32 位有符号整数范围内。
示例 1:
输入:k = 2, w = 0, profits = [1,2,3], capital = [0,1,1]
输出:4
解释:
由于你的初始资本为 0,你仅可以从 0 号项目开始。
在完成后,你将获得 1 的利润,你的总资本将变为 1。
此时你可以选择开始 1 号或 2 号项目。
由于你最多可以选择两个项目,所以你需要完成 2 号项目以获得最大的资本。
因此,输出最后最大化的资本,为 0 + 1 + 3 = 4。
示例 2:
输入:k = 3, w = 0, profits = [1,2,3], capital = [0,1,2]
输出:6
提示:
1 <= k <= 10^5
0 <= w <= 10^9
n == profits.length
n == capital.length
1 <= n <= 10^5
0 <= profits[i] <= 10^4
0 <= capital[i] <= 10^9
题解
法一:贪心 + 优先队列 (+ 快速选择)
参考官方题解评论区下梦璃夜·天星
Tips
①*max_element(capital.begin(), capital.end())
找出capital中最大元素
②nth_element(profits.begin(), profits.begin() + k, profits.end(), greater());
函数:
nth_element(a, a+i, b) 是 对[a,b)范围内进行快速选择,使得下标为i的元素排序完毕
即对数组按照cmp规则排序后,下标为i的数,放在对应位置上。
不加上第四个变量,默认求第 k 小元素,第k 元素左边可无序 且元素大小皆比第 k 小元素小,
右边元素大小皆比第 k 小元素大
注意k 从零开始
类比延伸:找出第 k 大元素,则只需找出第n - k + 1 小元素(注:这里类比链表(长 n 链表)倒数第k个,即为正数第 n - k + 1)
nth_element(profits.begin(), profits.begin() + n - k, profits.end());//n为数组profits大小
或者我们可以将所有元素变为负数,则第 k 大则为第 k 小
加上第四个变量:若为greater()则找出第 k 大的元素,
③iota(iCapital.begin(), iCapital.end(), 0);
iota函数对一个范围数据进行赋值,从第三个参数的初始值开始
每次加 1
④sort(iCapital.begin(), iCapital.end(), [&capital](int a, int b){return capital[a] < capital[b];});
对iCapital数组起始位置,之末尾位置进行排序
不填sort会默认按数组升序排序
与C语言中的qsort不同的是,对于C语言中的qsort中的第四个参数cmp来说,若 返回一个大于零的数则升序排序,
对C++中的sort函数来说则相反,返回一个大于零的数字则降序排序
本题中sort则是第三个参数自定义一个函数,按照对capital中元素升序排序来改变iCapital中元素的顺序
即下标映射排序,就可以不需要建立一份数对来排序了
class Solution {
public:
int findMaximizedCapital(int k, int w, vector<int>& profits, vector<int>& capital) {
int n = profits.size();
if (w >= *max_element(capital.begin(), capital.end())){
nth_element(profits.begin(), profits.begin() + k, profits.end(), greater());
return w + accumulate(profits.begin(), profits.begin() + k, 0);
}
vector<int> iCapital(n);
iota(iCapital.begin(), iCapital.end(), 0);
sort(iCapital.begin(), iCapital.end(), [&capital](int a, int b){return capital[a] < capital[b];});
priority_queue<int> pq;
auto cur = iCapital.begin();
//注意*cur为iCapital排序的从左往右的元素,capital[*cur]则未排序
for (int i = 0; i < k; ++i){
while(cur < iCapital.end() && capital[*cur] <= w){
pq.push(profits[*cur++]);
}
if (pq.empty()){
return w;
}
w += pq.top();
pq.pop();
}
return w;
}
};
时间复杂度:O[(n + k) log n],对下标数组的映射排序,所需复杂度O(n logn),堆中添加元素的 n log n
所需次数与拿出最大值的复杂度O(k log n)
若考虑快速选择,同时用BFPRT优化应该是严格O(n),不然是平均O(n) 最坏无穷大
故中的时间复杂度仍为O[(n + k)log n]
空间复杂度:O(n),所需映射下标数组大小与大根堆大小
法二:利用堆的贪心算法
Tips
priority<int>
默认的大根堆实现,内部从大到小排序,top()是当前优先队列的最大值
priority_queue<int,vector<int>,greater<int>>
最小值的优先队列,内部从小到大排序,top() 是当前优先队列的最小值。
priority_queue<int, vecotr<int>, less<int>>
最大值的优先队列,内部从大到小排序,top()为当前队列的最大值
参考官方题解
思路:
①假设不限制次数,我们启动所有满足 w >= capital[i]的项目,
来获取对应的profits[i]
②在 k 次条件下, 为了获取最大profits我们会 贪心 的
选择在所有满足 w >= capital[i] 中 前 k 项 最大值,
而数据结构大根堆pq可以做到这点
算法细节:
具体地,我们对capital与其对应的 profits 进行排序,使用capital为第一成员,
在 当前资金 w 的情况下,我们将所有 w >= capital[i]不同项目存储到大根堆中,
直到不满足 w >= capital[i]
若堆不为空则:
更新资金为w + pq.top(), 由于选择不同项目并删除堆顶元素,
若堆为空则:
说明当前资金无法完成任意项目, 跳出循环
typedef pair<int, int> pii;
class Solution {
public:
int findMaximizedCapital(int k, int w, vector<int>& profits, vector<int>& capital) {
int n = capital.size();
int cur = 0;
//优先队列,大根堆
priority_queue<int, vector<int>, less<int>> pq;
//记录项目与 对应资金
vector<pii> arr;
for (int i = 0; i < n; ++i){
arr.push_back({capital[i], profits[i]});
}
//我们对第一成员capital进行排序
sort(arr.begin(), arr.end());
for (int i = 0; i < k; ++i){
//当前资本w可以完成的项目,对应利润存储到大根堆中
while(cur < n && arr[cur].first <= w){
pq.push(arr[cur].second);
++cur;
}
//在堆不为空的条件下,拿到最大利润,即堆顶元素
if (!pq.empty()){
w += pq.top();
pq.pop();
}
else {
//任意项目都无法开展,结束选择
break;
}
}
return w;
}
};
以上是关于《LeetCode之每日一题》:143.IPO的主要内容,如果未能解决你的问题,请参考以下文章