1. 程式人生 > 實用技巧 >演算法—第四周(8月15日——8月21日)

演算法—第四周(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;
}