LeetCode 502. IPO

這周老師講解了一下貪心算法(greedy),我科對貪心的解釋是

貪心算法(又稱貪婪算法)是指,在對問題求解時,總是做出在當前看來是最好的選擇。也就是說,不從整體最優上加以考慮,他所做出的是在某種意義上的局部最優解。

貪心算法不是對所有問題都能得到整體最優解,關鍵是貪心策略的選擇,選擇的貪心策略必須具備無後效性,即某個狀態以前的過程不會影響以後的狀態,只與當前狀態有關。

大概就是這樣,按照慣例,我們找到了在LeetCode上tag爲greedy的題目中找到了這一道難度爲hard通過率最高的題,也就是這一道題目爲IPO的題,下面是題目

--------------------------題目在此-------------------------

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:

  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是真的一竅不通,我們直接看重要的部分吧,給了兩個整數W,K,兩個向量Profits和Capital,Profits和Capital是一一對應的,即我花費Capital【i】這麼多本金就可以得到Profits【i】這麼多純利益,我最多選擇k個不同的項目,也就是選擇不同的i,我的初始本金是W,當然了,當我獲得純利益後可以直接加到W中,並繼續執行,得到一個最大的W。

題目看起來挺複雜的,既然是貪心算法,現在就是要選擇一個策略了,策略倒是挺容易想的,每次我找到滿足我當前本金W能夠運營的所有的項目,選一個純利益最大的就好了。這種策略看起來就夠貪心了,但是我沒想到的是這道題還要更貪。

我前後使用了三種方法來解決這道題,有的新觀衆可能就震驚了,沒有想到我一個大學生還保持着初高中對知識的初心,追求一題多解。當然了,老觀衆是知道我的,我這麼懶一個人怎麼可能幹這麼無聊的事情,所以答案就是前兩次錯了,我要說的也就是這道題真正貪心的地方,我的前兩種方法超時了!!!對,你沒有看錯,超時了。所以這道題是真的貪心啊。

我的第一種方法的思路是這樣的,建立一個Capital到一個Capital對應的所有的Profits所組成的一個stack,這個stack是一個有序的,即最上面的元素是最大的,然後再對Capital排序,每次我只要找到所有小於W的Capital對應的stack的頂端元素的最大值,加到W上,並且將這個元素彈出stack。就是這樣的一種方法在倒數第二個點超時了,預處理的時間複雜度是O(n),後面查詢的時間複雜度O(n^2)

第二種方法和第一種主要思路是差不多的,進行了一點常數級優化,先用桶排序對Capital和Profits組成的一個pair進行排序,這樣一來Capital從小到大排,相同Capital對應的Profits從大到小排,再用隊列來存一個Capital對應的所有Profits,並此時Profits已經是從大到小排列了。與第一種方法同樣查詢,依舊在倒數第二個點超時了。

這是我就已經意識到了問題的所在了,就是查詢是那O(n^2)的複雜度,不能保證Capital越大,對應的Profits也越大,所有隻能從最小的Capital找到小於等於W的所有Capital,再找這個區間內所有Profits最大的一個,我能怎麼辦?我也很無奈啊。既然已經發現問題了,那麼就想辦法解決問題唄,之前想的都是每查詢完一次下一次就重新查詢,直接導致了O(n^2)的時間複雜度,那麼我要是隻是遍歷一次Capital的向量複雜度應該就OK了吧,那麼查詢問題怎麼解決呢?我需要每次都能得到目前訪問位置之前的最大的一個Profits,而且還要在使用完後刪除。答案就是優先隊列,或者說集合也可以,這裏我選擇了使用集合,預處理我也想了一個新的更直觀的方法來做。下面直接上代碼吧。

------------------------------下面是代碼------------------------

#include <iostream>
#include <vector>
#include <memory.h>
#include <map>
#include <algorithm>
#include <stack>
#include <queue>
using namespace std;
class Solution {
public:
	static bool cmp(pair<int,int>a,pair<int,int>b)
	{
		return a.first<b.first;
	}
    int findMaximizedCapital(int k, int W, vector<int>& p, vector<int>& c) {
        vector<pair<int,int> >indexc;//建立Capital到源向量下標的映射 
        int t=p.size() ;
        for(int i=0;i<t;i++)
        {
        	pair<int,int> temp;
        	temp=make_pair(c[i],i);
        	indexc.push_back(temp); 
		}
		stable_sort(indexc.begin() ,indexc.end(),cmp);//使得Capital從小到大排列 
		priority_queue<int> pp;
		int i=0;
		while(k--)
		{
			for(;i<indexc.size() &&indexc[i].first<=W;i++)
			{
				int index=indexc[i].second;
				int pi=p[index];
				pp.push(pi);
			}
			if(pp.empty())
			break;//如果優先隊列空則表明沒有滿足W的Capital了,這個時候也只能結束了 
			W+=pp.top();
			pp.pop();//刪除當前最大元素 
		}
		return W; 
    }
};
代碼很簡短也很好理解,這道題真的是讓我體會到了什麼叫貪心,你說你都貪心了還要時間複雜度低,我是真的服。使用優先隊列後整個的時間複雜度應該就變爲了O(nlogn)的複雜度了,不過好歹是終於已經過了,希望小夥伴不要像我一樣掉到坑裏去了。這次的題解就到這裏了。

----------------------------------------手動分割線----------------------------------

see you next illusion




發佈了32 篇原創文章 · 獲贊 6 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章