每日一题–502
记一下我今天曲折地做这条题目的路
前言:题目介绍
原题是这样的502. IPO - 力扣(LeetCode) (leetcode-cn.com)
1. 想都不想 直接贪心算法
是的,对于一个题目当然要来点贪心算法来理解一下流程了(bushi),流程如下图所示:
所以就手撸了下面的贪心算法,然后果然不出所料的,超时了。
int findMaximizedCapital(int k, int w, vector<int>& profits, vector<int>& capital) {
//每次都在可选列表中找到边际收益最大的
vector<int> projects,remains;
//循环时间复杂度为n
for(int i = 0; i < profits.size() ; i++)
{
if(capital[i] <= w) projects.push_back(i);
else remains.push_back(i);
}
int max_arg,max_pro;
// 循环时间复杂度为 k * n
for(int times = 0; times < k ; times++ ){
//找到最高边际收益
max_pro = max_arg = -1;
cout << "备选projects:" ;
show_voctor(projects);
//循环时间复杂度为 n
for(int i = 0 ; i < projects.size(); i++){
if(profits[projects[i]] > max_pro){
max_arg = i;
max_pro = profits[projects[i]];
}
}
cout << "选中的 project:";
cout << projects[max_arg] <<endl;
if(max_pro >= 0)
{
w += max_pro;
projects.erase(projects.begin() + max_arg);
//添加剩下的
int remains_size = remains.size();
// 复杂度为n
for(int i = 0; i < remains_size ; i++)
{
if(capital[remains[i]] <= w) projects.push_back(remains[i]);
else remains.push_back(remains[i]);
}
for(int i = 0; i < remains_size ; i++)
{
remains.erase(remains.begin());
}
cout << "第" << times << "次投资结束\n" <<endl;
}
else break;
}
cout << w << endl;
return w;
}
2. 果断开始优化时间复杂度
是的既然超时了就要想办法来简化时间复杂度,于是我天王盖地虎宝塔镇蛇妖一顿优化呀。
最好优化的一部分就是能够被使用的数据部分,排序后,备选项目中加入新的内容就不需要再进行一次遍历了,而直接进行选择就可以了。于是我就进行了归并排序,但这里其实还有一点问题,就是在选择了备选的内容后仍然进行了一次基于利润的归并排序,这里可能存在一定的问题。虽然但是,问题却没出在时间复杂度上,而是很奇怪的问题。
这个bug真的很让人难受,主要是,这里面几万条数据我也没办法找到问题出在哪里。
3. 换了有序二叉树
虽然不知道为什么会出现上面奇奇怪怪的问题,我猜测可能是第二次归并排序的时候出现了问题,这时候就凭借我还有的一点点数据结构与算法的知识,想起来了有一个东西叫有序二叉树,这东西如果我每次添加一个节点的话,时间复杂度一般是log2n,而弹出最大的数也一般是log2n,于是我就手撸出来了一个二叉树(惊叹于这竟然能跑)。
然后!!刚刚的问题解决了,我大惊失色,果断提交!
结果,又双叒叕超时了。
于是我点开了数据,我nm!
是的,如图所示,数据有一半都是10000,二叉树结构直接被逼到最坏情况。(我采用的是左子树小于,右子树大于等于的策略)
这怎么办呢?简直太可怕了吧!
于是我,直接给每一个节点加了个计数功能。
treeNode(int val,treeNode* left = 0, treeNode* right = 0,int num = 1):\
val(val),left(left),right(right),num(num){}
int val,num; //num表示有几个这个数字
treeNode *left,*right;
};
加了计数功能之后,终于顺利解决了问题。
具体代码如下
void merge_sort(vector<int>& reference, int* ordered_args, int len){
//reference 是数值的参考对象,ordered_args是用来存储排序后的顺序
//这部分算法借鉴了菜鸟教程 https://www.runoob.com/w3cnote/merge-sort.html c++ 迭代版
int* a = ordered_args;
int* b = new int[reference.size()];
//初始化索引表
for(int i = 0; i < len; i++) a[i] = b[i] = i;
for(int seg = 1; seg < len ; seg *= 2){
for (int start = 0; start < len; start += seg + seg) {
int low = start, mid = min(start + seg, len), high = min(start + seg + seg, len);
int k = low;
int start1 = low, end1 = mid;
int start2 = mid, end2 = high;
while (start1 < end1 && start2 < end2)
b[k++] = reference[a[start1]] < reference[a[start2]] ? a[start1++] : a[start2++];
while (start1 < end1)
b[k++] = a[start1++];
while (start2 < end2)
b[k++] = a[start2++];
}
int *temp = a;
a = b;
b = temp;
}
if (a != ordered_args) {
for (int i = 0; i < len; i++)
b[i] = a[i];
b = a;
}
delete[] b;
}
class OBT//order binary tree,右侧大于等于根
{
public:
class treeNode{
public:
treeNode(int val,treeNode* left = 0, treeNode* right = 0,int num = 1):\
val(val),left(left),right(right),num(num){}
int val,num; //num表示有几个这个数字
treeNode *left,*right;
};
private:
treeNode* root = 0;
public:
void insert(int num){
if(!root) {
root = new treeNode(num);
assert(root);
return;
}
if(num < root->val) insert(num, root->left);
else if(num > root->val) insert(num, root->right);
else root->num++;
}
void insert(int num, treeNode* &root){
if(!root) {
root = new treeNode(num);
assert(root);
return;
}
if(num < root->val) insert(num, root->left);
else if(num > root->val) insert(num, root->right);
else root->num++;
}
int pop_max(){
int value;
treeNode *temp = root;
if(root->right == NULL){
root->num--;
value = root->val;
if(!root->num){
root = root->left;
delete temp;
}
return value;
}
treeNode* node = root;
while(node->right) {
temp = node;
node = node->right;
}
value = node->val; //备份需要获取的数值
node->num--;
//删除节点
if(!node->num){
temp->right = node->left;//有就赋值过去了
delete node;
}
return value;
};
bool is_empty(){
return root == NULL;
}
void show()
{
if(root->right) show(root->right);
cout << root->val << ' ';
if(root->left) show(root->left);
}
void show(treeNode* root){
if(root->right) show(root->right);
cout << root->val << ' ';
if(root->left) show(root->left);
}
};
int findMaximizedCapital(int k, int w, vector<int>& profits, vector<int>& capital) {
//先根据capital排序
int* ordered_args = new int[capital.size()];
merge_sort(capital,ordered_args, capital.size());
//for(int i = 0 ; i < capital.size() ; i++) cout << capital[ordered_args[i]] <<endl;
if(capital[ordered_args[0]] > w) return w;
int now_arg = 0; //表示第几个位置之前的项目都有足够资金做了
int times = 0; //表示选择了几个项目了
OBT available_proj;
//最外层循环,选中足够数量的项目即可结束
while(times < k){
//指针右移到能够买的位置
for(; now_arg < capital.size()?capital[ordered_args[now_arg]] <= w:false ;\
now_arg++ ) available_proj.insert(profits[ordered_args[now_arg]]);
//根据利益进行排序
if(available_proj.is_empty()) return w;
w += available_proj.pop_max();
times++;
}
return w;
}
5. 看了题解,有被伤害到
看了下题解,大家用的都是优先级队列,把我手敲了n长的代码给用库代替了,o(╥﹏╥)o认栽了,确实是我对cpp不够熟悉,这些内置的库都不太清楚。但我很奇怪,内置的优先级队列在我看到的blog里(未深究)是使用的最大堆,理论上跟普通二叉树的表现应该差不多,但是实际上就不会超时呢?不过手敲一敲数据结构还是挺有意思的。