14.6 問題 5 瓶詰め問題解いた
読み終わったけど,今朝やってみたら意外と解けたので書いておく.
ヒープは簡単に実装できる割に凄い.でもプライオリティキューとか聞くとどうしても,
スケジューラ->形式検証必須(平等とか)->手が出せないorz みたいな印象が…なぜだろう?
問題
それぞれ0から1までの間の大きさ(ウエイト)を持ったn個の要素があったとし、
それを容量が1のビンに詰めていくとする。
このとき、なるべくビンの数を少なくするにはどうしたらよいか…
解法
ビンの空き容量で順序づけた要素をヒープに入れ,
出来る限り左(下)のビンに詰めていく.空いてなければ右に.左右とも空いてなければその親要素に詰める.
…というかんじで実装した.(ファーストフィット法と云うらしい)
// 使い方 int main(){ int N=100; FstFit h(N); for(int i=0; i<N; i++){ h.insert(0); // 何も入ってない状態のビンをN個登録 } for(int i=0; i<N; i++){ int x=N*(double)rand()/RAND_MAX; h.pack(x); // 詰めます. 超詰めます. } h.print(); }
実装
ヒープクラス概要
class Heap { protected: int * xs_; // データ(xs_+1以降しか使用しない) int len; // 保持出来るデータ数 int size_; // 保持しているデータ数 void shiftup(int N); void shiftdown(int N); public: Heap(int N) : xs_(new int[N+1]), len(N+1), size_(0) {} // 良きにはからへ void print(void) const; int get_size() const { return size_; } void insert(int v); int get_min(void); ~Heap() { delete xs_; } };
実際にびんずめする部分
class FstFit : public Heap { int weight_; // ビンの容量 public: FstFit(int N) : Heap(N), weight_(N) {} void pack(int w); }; void FstFit::pack(int w){ int N=1; while(N<size_/2){ int left=N*2; int right=N*2+1; if(xs_[left]+w<=weight_){ // 左に詰められる N=left; }else{ if(xs_[right]+w<=weight_){ // 右に詰められる N=right; }else{ break; // どっちもいっぱい\(^o^)/ } } } xs_[N]+=w; // ウエイトを足し込んで, shiftdown(N); // ヒープを再構築 }