用最小堆解決一個實際問題

問題描述如下:

 

一個公司要給多個部門分配辦公室空間,總的空間是一定的,分配空間所需的時間正比於空間的大小

比如, 總的空間的大小是1000,需要花費1000個單位時間來劃分這些時間(不管是劃分成300+700,還是劃分爲500 + 500,都是花費相同的時間)

 

但是總的時間取決於劃分的順序

比如總的空間數是800,每個部門的空間大小是100,200,500

如果首先劃分成100和700, 然後再把700劃分成200+500, 則總的時間是800+700 = 1500

但是如果首先劃分爲500和300,然後把300劃分成100+200,則總的時間是800+300= 1100

可見不同的劃分方法,所用的時間是不一樣的,現在要求完成最終的劃分所需的最少時間。

 

思路:

每次從劃分集合中刪除最小的二個數,將這二個數之和加到總時間中去,同時二個數之和作爲一個新的數放到劃分集合中去,再重複以上步驟,直到最後剩下一個數

那麼用什麼數據結構來取最小的二個數最方便呢?這裏選擇用最小堆

 

實現:

 

#include <cstdio>
#include <iostream>

using namespace std;


template <typename T>   // 定義一個模板類

class MiniHeap
{



public:

     MiniHeap(int n);

     ~MiniHeap();

     T pop();  // 彈出並返回堆頂的元素,也就是最小的元素

     void push(T key);     // 將key這個元素插入到堆中,並且插入後要保證是最小堆

     int size() ;

     void print();

private:

     void bottomAdjust(int pos); //從某個位置開始從下往上調整

     void topAdjust(int start, int end); //從堆頂位置從上往下調整

private:

     T* heap;  //指向堆數組的指針

     int curSize;  // 指示當前有多少個數在堆中,同時指示下一個要入堆的元素在調整前應該存放的位置

     int maxSize;  // 最小堆中最多能存放多少個元素

};

// 初始化一個堆
template <typename T>  // 模板類實現方法
MiniHeap<T>::MiniHeap(int n)
{
     maxSize = n;
     curSize = 0;
     heap = new T[maxSize];
}

template <typename T>
MiniHeap<T>::~MiniHeap()
{
     curSize = 0;
     maxSize = 0;
     delete [] heap;
}

template <typename T>
int MiniHeap<T>::size()
{
     return curSize;
}

template <typename T>
void  MiniHeap<T>::print()
{
     int i = 0;
     for(; i < curSize; i++)
     {
          cout << heap[i] << " ";
     }
     cout << endl;
}


template <typename T>
T MiniHeap<T>::pop()
{
     // 取堆頂元素返回

     T top = heap[0];

     // 刪除堆頂的元素,就是將最後的元素覆蓋上來,進行一次至上而下的調整

     curSize --;

     heap[0] = heap[curSize];

     topAdjust(0,curSize-1);

     return top;

}

template <typename T>  // 每個方法的實現,都有template < typename T> 在前面表示這是一個模板類的方法,模板類型T
void MiniHeap<T>::topAdjust(int start, int end)
{

     int parent = start;

     int child = 2 * parent + 1; 

     T temp = heap[parent];  //先保留這個元素,等到至上而下調整完畢之後,在將它放入合適的位置
 
     while( child <= end)  //循環可能在循環終止條件退出,也可能在中間的某個時候退出
     {
          // 從上往下,跟top比較
          if( child  < end && heap[child + 1]  < heap[child])
          {
               child++;  // 將左右孩子中的較小的一個換上來
          }

          if( temp < heap[child])  // 如果某個時候,先前保留的元素已經小於child,那說明這個temp值應該放在parent位置上
          {
               break;
          }

          else
          {

               heap[parent] = heap[child];

               parent = child;

               child = 2 * child + 1;
          }
     }

     heap[parent] = temp;

}

// 假設目前堆空間足夠插入
template <typename T>
void MiniHeap<T>::push( T key)
{
     heap[curSize] = key;
     bottomAdjust(curSize);
     curSize++;
}

template <typename T>
void MiniHeap<T>::bottomAdjust(int pos)
{

// 從 pos 開始從下往上調整,跟父親比,如果比父親小則跟父親交換

     int child = pos;

     int pivot = heap[pos];

     int parent = (child-1)/2;

     while( child > 0 && pivot < heap[parent])  // 如果要調整的元素比它的父親小  // 如果這二個條件中有一個條件不滿足了,就退出了循環
     {
          // 將父親拉下來

          heap[child] = heap[parent];

          child = parent;

          parent = (parent-1)/2;

     }

     heap[child] = pivot;

}


int N;
int data[10001];
int ptime;

int main(int argc, char** argv)
{
     int test_case;
     int T;
    
     const char* INPUTFILE(TCDIR "sample_input_case.txt");
     cout << INPUTFILE << endl;
     freopen(INPUTFILE,"r",stdin);

     cin >> T;
     for(test_case = 0; test_case < T; test_case++)
     {

          int i;
          cin >> N;

          MiniHeap<int>* heap = new MiniHeap<int>(N);

          for(i=1; i<=N; i++) {
               cin >> data[i];
               heap->push(data[i]);
               // heap->print();
          }


          ptime = 0;

          while( heap->size() > 1)
          {
               int a = heap->pop();

               int b = heap->pop();

               int partition = a+b;

               ptime += partition;

               heap->push(partition);

          }

          cout << ptime << endl;

     }

     return 0;
}

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章