Week8

Week8

Greedy
question source: IPO

question description

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.

Given an array of non-negative integers, you are initially positioned at the first index of the array.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:

  1. You may assume all numbers in the input are non-negative integers.
  2. The length of Profits array and Capital array will not exceed 50,000.
  3. The answer is guaranteed to fit in a 32-bit signed integer.

这是一道标签为贪心算法的题。这周继续学习了贪心算法的一些内容。这道题是说,公司要进行IPO(首次公开募股),要做项目来提高公司的总价值。给出若干个项目的启动金额和利润,给出你可以做的项目的个数和初始金额,求公司最大的总价值是多少。

解决方法

这很明显能用贪心算法来解决,每一次都选你能做的项目中的有最大利润的那一个项目。想法很简单朴素。

实现代码如下

class Solution {
public:
    int findMaximizedCapital(int k, int W, vector<int>& Profits, vector<int>& Capital) {
        int visited[Capital.size()];
        memset(visited, 0, sizeof(visited));                                                                         
        
        for(int i = 0; i < k; i++){
        //find the best profit
            int best_profit = 0;
            int best = -1;
            for(int j = 0; j < Capital.size(); j++){
                if(visited[j]) continue;
                if(W >= Capital[j]){
                    if(Profits[j] >= best_profit){
                        best_profit = Profits[j];
                        best = j;
                    }
                }
                
            }
            if(best == -1) break;           
            visited[best] = 1;
            W += best_profit;
        }
        return W;
    }
};

每次都遍历一次数组,找到能还没做过的项目中,目前资金能启动中收益最大的那个项目,重复k次。没错,错是没有错,只是不通过,Time Limit Exceeded,给出的样例是5000个项目,每次都要遍历一次数组,这太耗时间了。

于是就要改进算法了,就是要改进优先队列的实现方式,上面的方法是用一个无序数组来实现的。书上提过可以用二分堆的方法来实现。我就自己实现了一下堆结构。二分堆的元素在一个完全二叉树中。这是一棵最大堆:树中的任意节点的键值都必须大于等于其后裔的键值。所以树的根节点就一定是树的最大的元素。所以算法就是维护一个二分堆,二分堆保存的是可做项目的利润,每次取根节点作为此次要做的项目,做完后,再把能做的项目放进堆里维护。

实现代码如下

int findMaximizedCapital(int k, int W, vector<int>& Profits, vector<int>& Capital) {
    
        for(int i = 0; i < Capital.size(); i++){
                        
            pair<int, int> p(Capital[i], Profits[i]);
            v.push_back(p);
        }
        // 根据启动金额排序
        sort(v.begin(), v.end());
        
        int pos = -1;
        for(int i = 0; i < k; i++){          
            for(int j = pos + 1; j < v.size(); j++){
                if(W >= v[j].first){               
                    insert(heap, v[j].second);
                    pos = j;
                }else{                  
                    break;
                }
            }

            if(heap.empty()) break;
            W += heap[0];           
            deletemax(heap);
            
        }
        return W;
    }

进行insert操作,将新元素放在最底端(最底端最先可用的位置),然后 “冒泡” :如果新元素比其父节点要大,则将新元素与其父节点交换,并重复其过程。交换的次数最多为树的高度。位置为j的节点,父节点为j/2。(1 <= j <= size)。

void insert(vector<int>& heap, int n){
        heap.push_back(n);
        //bubble
        int i = heap.size();
        if(i == 1) return;
        while(heap[i - 1] > heap[i / 2 - 1]){
            int tem = heap[i - 1];
            heap[i - 1] = heap[i / 2 - 1];
            heap[i / 2 - 1] = tem;
            i /= 2;
            if(i == 1 || i == 0) break;
        }
    }

deletemax操作,直接返回根节点的键值,将根节点对应元素从堆中删除之后,取出树中的最后一个节点,将它置于树根上,再令它 “向下过滤” :如果节点的键值小于它的任何一个子节点,则将它与较大的子节点进行交换,并重复此过程。也是最多交换次数为树的高度。位置为j的子节点的位置分别为2j和2j+1。(1 <= j <= size)


    void deletemax(vector<int>& heap){
        int size = heap.size();
        if(size == 0) return;
        if(size == 1) {
            heap.pop_back();
            return;
        }
        heap[0] = heap.back();
        heap.pop_back();
        size--;
        //sink
        int i = 1;
        while(true){
            //no kid
            if(2 * i > size) break;
            //only have left
            else if(2 * i <= size && 2 * i + 1 > size){
                if(heap[2 * i - 1] > heap[i - 1]){
                    int tem = heap[2 * i - 1];
                    heap[2 * i - 1] = heap[i - 1];
                    heap[i - 1] = tem;
                    i = 2 * i;
                }else{
                    break;
                }
            }
            //have left and right
            else{
                //swap left
                if(heap[2 * i - 1] > heap[i - 1] && heap[2 * i - 1] >= heap[2 * i]){
                    int tem = heap[2 * i - 1];
                    heap[2 * i - 1] = heap[i - 1];
                    heap[i - 1] = tem;
                    i = 2 * i;
                }
                //swap right
                else if(heap[2 * i] > heap[i - 1] && heap[2 * i] >= heap[2 * i - 1]){
                    int tem = heap[2 * i];
                    heap[2 * i] = heap[i - 1];
                    heap[i - 1] = tem;
                    i = 2 * i + 1;
                }
                else{
                    break;
                }
                
            }
        }
    }

实现的思路是很清晰的,但是在真正实现的时候就会有很多问题。尤其是数组越界问题,以至于要加很多判断条件。现在自己实现一次就清晰很多了。
C++里面有一种STL容器叫priority_queue,用它就很方便,底层实现也是用堆。但我是想自己用堆来实现一个,加深一下印象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值