演算法—第四周(8月15日——8月21日)
貪心演算法
分金條問題
會議室場次問題
花費資金做專案問題
取中位數問題
分金條問題
一塊金條切成兩半,是需要花費和長度數值一樣的銅板的。比如長度為20的金 條,不管切成長度多大的兩半,都要花費20個銅板。
一群人想整分整塊金條,怎麼分最省銅板? 例如,給定陣列{10,20,30},代表一共三個人,整塊金條長度為10+20+30=60。 金條要分成10,20,30三個部分。 如果先把長度60的金條分成10和50,花費60; 再把長度50的金條分成20和30,花費50;一共花費110銅板。 但是如果先把長度60的金條分成30和30,花費60;再把長度30金條分成10和20, 花費30;一共花費90銅板。 輸入一個數組,返回分割的最小代價
思路:
首先將陣列的值進到優先順序佇列裡去,有一個小到大的排序過程,然後依次取最大的兩個值進行計算代價
然後將計算完成之後的結果再放入佇列尾部,不斷重複,一直到佇列為空。
每次累加最後返回的值,就是分割的最小代價。
int lessMoney(int array[],int len) { priority_queue<int> pQ; for (int i = 0; i < len; i++) { pQ.push(array[i]); } int sum = 0; int cur = 0; int a = 0; while (pQ.size() > 1) { a = pQ.top(); pQ.pop(); cur = a + pQ.top(); pQ.pop(); sum += cur; pQ.push(cur); } return sum; }
會議室場次問題
一些專案要佔用一個會議室宣講,會議室不能同時容納兩個專案的宣講。 給你每一個專案開始的時間和結束的時間(給你一個數組,裡面是一個個具體的專案),你來安排宣講的日程,要求會議室進行的宣講的場次最多。 返回這個最多的宣講場次。
思路:
首先定義專案類,開始時間和結束時間,按結束時間進行從小到大的排序
start是可以開始的時間,傳參進來為0,從0點開始。
如果start的時間是在當前的專案的時間之前或者剛剛好,則可以開展的場次結果加1,並且將這次結束時間作為下次可開始的時間。
class Program { public: Program(int s,int e):start(s),end(e){} int start; int end; }; bool compare(Program a, Program b) { return a.end < b.end; } int bestArrange(Program program[], int start) { int len = sizeof(program) / sizeof(program[0]); sort(program, program + len, compare); int result = 0; for (int i = 0; i < len; i++) { if (start <= program[i].start) { result++; start = program[i].end; } } return result; }
花費資金做專案問題
輸入:
正數陣列costs
正數陣列profits
正數k
正數m
含義:
costs[i] 表示i號專案的花費,profits[i] 表示i號專案在扣除花費之後還能掙到的錢(利潤) ,k表示你只能序列的最多做k個專案 ,m表示你初始的資金
說明: 你每做完一個專案,馬上獲得的收益,可以支援你去做下一個專案。
輸出: 你最後獲得的最大錢數。
思路:
首先定義一個類,包含花費和利潤。
然後建立一個花費的小根堆,一個利潤的大根堆。為了後續花最少的錢賺更多的錢
然後依次將花費表和利潤表放入小根堆裡去。
然後根據最多做的專案數量來進行迴圈,在小根堆不為空的情況下把所有買得起的全扔進利潤大根堆裡面去。
然後進行資金的累加,將大根堆第一個的利潤直接加到初始資金上。
有一個邊界考慮,當大根堆為空的時候,然後還沒有達到做大次數,這個時候就說明了錢不夠,再怎麼樣也做不完所有專案,所以直接返回當前的資金就可以了
class Node { public: Node(int p,int c):profit(p),cost(c){} int cost; int profit; }; int findMaximizedCapital(int k, int w, int profits[], int capital[],int len) { priority_queue<Node*,vector<Node*>,greater<Node*>> minCostQ;//小根堆 priority_queue<Node*,vector<Node*>,less<Node*>>maxProfitQ;//大根堆 for (int i = 0; i < len; i++) { minCostQ.push(new Node(profits[i], capital[i])); } for (int i = 1; i <= k; i++) { while (!minCostQ.empty() && minCostQ.top()->cost <= w) { maxProfitQ.push(minCostQ.top()); minCostQ.pop(); } if (maxProfitQ.empty()) { return w; } w += maxProfitQ.top()->profit; maxProfitQ.pop(); } return w; }
取中位數問題
一個數據流中,隨時可以取得中位數
思路:
用一個小根堆和一個大根堆進行不斷的比較交換,最後獲得中位數。
第一個值直接進大根堆,剩下的值如果是小於等於大根堆的堆頂,就進入大根堆,否則進入小根堆。
我畫了一個圖來表明上面的這個操作,我用的佇列來畫的圖,這個不是堆的圖。只是為了看的更加直觀
從這個圖可以很直觀的看出來,大根堆和小根堆的關係
如果當大根堆或者小根堆裡面的元素個數相差2個,就從元素多的那個堆中頂部取一個值放入元素少的那個堆中。
最後判斷兩個堆中的元素個數是否一樣,如果一樣就兩個頂部相加除2。
如果不一樣,就直接取元素多的那個堆的頂部
priority_queue<int, vector<int>, less<int>>maxHeap; priority_queue<int, vector<int>, greater<int>>minHeap; void heapChange() { if (maxHeap.size() == minHeap.size() + 2) { minHeap.push(maxHeap.top()); maxHeap.pop(); } if (minHeap.size() == maxHeap.size() + 2) { maxHeap.push(minHeap.top()); minHeap.pop(); } } void addNumber(int num) { if (maxHeap.empty() || num <= maxHeap.top()) { maxHeap.push(num); } else minHeap.push(num); heapChange(); } int getMid() { int maxSize = maxHeap.size(); int minSize = minHeap.size(); if (maxSize == 0 && minSize == 0) { return 0; } int minHeapHead = minHeap.top(); int maxHeadHead = maxHeap.top(); if (maxSize == minSize) { return (minHeapHead + maxHeadHead) >> 1; } return maxSize > minSize ? maxHeadHead : minHeapHead; }