[LeetCode解题报告] 502. IPO

题目描述

假设 LeetCode 即将开始其 IPO。为了以更高的价格将股票卖给风险投资公司,LeetCode希望在 IPO 之前开展一些项目以增加其资本。 由于资源有限,它只能在 IPO 之前完成最多 k 个不同的项目。帮助 LeetCode 设计完成最多 k 个不同项目后得到最大总资本的方式。

给定若干个项目。对于每个项目 i,它都有一个纯利润 Pi,并且需要最小的资本 Ci 来启动相应的项目。最初,你有 W 资本。当你完成一个项目时,你将获得纯利润,且利润将被添加到你的总资本中。

总而言之,从给定项目中选择最多 k 个不同项目的列表,以最大化最终资本,并输出最终可获得的最多资本。

示例 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。

注意:

  1. 假设所有输入数字都是非负整数。
  2. 表示利润和资本的数组的长度不超过 50000。
  3. 答案保证在 32 位有符号整数范围内。

算法

这题我选择用堆排序来做,具体流程如下:
目标:最大化当前持有的资本

  1. 启动资本获利打包在一个节点中再进行排序.具体的排序为按照启动资本从小到大排序,如果相等的启动资本,则按照获利从大到小排序
  2. 循环遍历打包排序好的数组:将当前持有的资本目前遍历到的节点所需的启动资本比较:
    1. 如果当前持有的资本大于或等于节点所需的启动资本:将该节点能获取的利润插入堆中,并将堆调整维护为一个最大堆(这样能保证堆顶始终存放着能以持有的资本获取的最大利润
    2. 如果当前持有的资本小于节点所需的启动资本,更不用说后面的那些节点了,停止遍历,将堆顶存放的值加到当前持有的资本中,并将堆顶删除
    3. 当前持有的资本已经发生变化,继续在数组从刚才的位置往后遍历,直到选择完给定的k个项目

边界条件

  1. 如果一开始给定的当前持有的资本比数组中最前面的那个节点带有的启动资本都要小,也就是说以当前持有的资本根本选择不了任何一个启动项目,直接返回当前持有的资本
  2. 如果给定的可选k个项目比总的项目还要多,那么k值要变成中的项目数。因为总共就这么多,不可能选出比总数还多的项目来。

代码

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

// 将启动资本与获利打包在一个节点上
struct Node
{
    int _p, _c;
};

// 自定义的排序流程
bool cmp(const Node &a, const Node &b)
{
    bool flag;
    if (a._c < b._c)
        flag = true;
    else if(a._c > b._c)
        flag = false;
    else
        flag = (a._p > b._p) ? true : false;
    return flag;
}

class Solution {
public:
    int findMaximizedCapital(int k, int W, vector<int>& Profits, vector<int>& Capital) {
        int len = Profits.size();
        Node pack[len];
        // 将启动成本与收益打包在一起,之后排序
        for(int i = 0; i < len; i++)
        {
            pack[i]._p = Profits[i];
            pack[i]._c = Capital[i];
        }
        sort(pack, pack+len, cmp);

        // 边界条件,如果连最小的项目的启动成本都比W要大,那一开始就没有什么项目可选
        if (pack[0]._c > W)
            return 0;
        
        // 边界条件,如果可选项目k的个数大于给定的项目个数,k应该重新赋值
        if (k > len)
            k = len;
            
        int maxSum = W, i = 0;
        vector<int> max_heap;
        
        //开始算法
        while (k)
        {
            for (; i < len && pack[i]._c <= maxSum; i++)
            {
                // 插入堆的末尾,并且调整为最大堆
                max_heap.push_back(pack[i]._p);
                PercUP(max_heap);
            }

            // 因为此时堆顶就是目前利润最大的项目的利润值,将堆顶元素加到maxSum上去,并删除堆顶(与最后一个互换,然后继续调整最大堆)
            maxSum += max_heap[0];
            max_heap[0] = max_heap[max_heap.size()-1];
            max_heap.pop_back();
            PercDown(max_heap);
            k--;
        }
        return maxSum;
    }

    void PercUP(vector<int> &max_heap)
    {
        /*** 本函数将插至末尾的数向上调整至合适的位置以维护一个最大堆 ***/
        int child = max_heap.size() - 1;
        int X = max_heap[child], father;
        while (child != 0)
        {
            father = (child - 1) / 2;
            if (max_heap[father] >= X)
                break;
            max_heap[child] = max_heap[father];
            child = father;
        }
        max_heap[child] = X;
    }

    void PercDown(vector<int> &max_heap)
    {
        /*** 本函数将交换后的堆顶向下调整至合适的位置以维护一个最大堆 ***/
        int len = max_heap.size();
        if (len == 0)
            return;
        int father, child, X = max_heap[0];
        for (father = 0; father * 2 + 1 < len; father = child)
        {
            child = father * 2 + 1;
            if (child + 1 < len && max_heap[child] < max_heap[child+1])
                child++;
            if (X >= max_heap[child])
                break;
            max_heap[father] = max_heap[child];
        }
        max_heap[father] = X;
    }
};
int main()
{
    vector<int> profit = {1,2,3};
    vector<int> capital = {0, 1, 1};
    int k = 10, w = 0;
    Solution s;
    cout << "最大资本是:" << s.findMaximizedCapital(k, w, profit, capital) << endl;
    return 0;
}

最后

这题直接使用STL中的优先队列更方便,堆调整的算法也更好。

posted @ 2019-02-28 13:02  小王点点  阅读(338)  评论(0编辑  收藏  举报