トピックリンク https://www.luogu.org/problem/P2168
影響を受ける所与の nワード出現回数のWi、検索kの 最小全長バイナリプレフィックス変換後のコード、並びに確保その最も長い文字列のSiの最小の最短長の全長。
この問題は現在、非常に単純でNOIの観点とみなされる(私は実際にWを凹)、それは、これが大きな問題であると述べたが、困惑しました。それはとても一般的なハフマン木、そのようなことハフマン木の数とは思いませんでしたので、おそらく他、理解することは非常に簡単ではありませんので、最初はおそらく時間がかかりすぎて質問です。
まあ、タイトルを見てください。質問は非常に明確だった、目標は、符号化後の最小全長を作ることですので、今、私たちはできる限り低減されているコードの長さがより頻繁に表示させる方法を考える必要があります。しかし、タイトルには限界がある:K-進コーディングは任意のプレフィックス他のコードではありません。その結果、対象の練習は、ハフマン木を指します。
ハフマン木とハフマンコーディング
ここでは単に繰り返し - (証拠の引用をからの「データ構造、-C言語記述するために」庚国華編集者)
ハフマン木の特性:ルートから最短のすべてのリーフノードへの加重パスの長さ(つまり、大規模なルートを置くための適切な場所に近いです)
練習:最小の配列は、子ノードとしてのルートの重みの値を合併し、元のシーケンスのルートノードに加え、上述の動作が繰り返される2点を選択しました。
ハフマン符号化特性:1.それは接頭辞です。接頭コードは一種ではなく、任意の他の符号化符号化前の対象となるTのアイテム。証明:ハフマンコーディング配列のコーディングはエッジの配列に相当するパスの葉への根元側で、パスAが別のパスBの左端の一部である場合、木の特性から分かるように、その後、Aの後にBそのため、エンドは、リーフAではありません。ハフマン符号化のいずれかが完全に他のハフマン符号化の前部分と重ならないように、ハフマン符号化及び葉路の端部に相当するが、ハフマン符号化は、このように接頭コードです。
2.これは、最適な接頭コードです。それは:n文字のために、ハフマンツリー構造によってその使用頻度が葉であり、それは、符号化された様々なパケットに対応する最短長符号化を意味することができます。証明:ハフマンツリーの使用の各文字の右葉周波数に対応するハフマン符号、およびWは、i番目の文字の使用頻度最小量ツリー、すなわち最小ΣWPと木のため、長さがPは、平均式:パケット長の測定値であるi番目の文字コードの長さ、です。
役割:これは、データ圧縮技術である基本的な考え方えっ!
方法:物事を保存するために、ヒープを取ります。
さて、この質問は、ハフマン木と完全に一致アイデアで見つけることができます。そして、ハフマンは良くしようとするコーディング。。
大物は、非常によくこの質問を分析する必要があるので、私はちょうど良い水を引きます。
ハフマンツリーはバイナリツリーです。そして、この質問は、多分岐ツリーに与えられています。まあ、実際に拡大しています。
私たちは、井戸建設ハフマン木の世話を想像してみてください。まず、木の深さは言葉の最上点の長さで、最も頻繁にエンコードされた単語が表示され、下部には倍の最小数を登場。それはこれです、話すようにサンプルを取ります:
こうして標準コードは、それぞれ00、四点です。
ハフマンの考えによると、それぞれが最終的にすべてのノードが一つに結合されるまで、右の彼らの親ノードのいずれかにポイントするために、2つの現在のポイントの最小値を取る必要があります。この質問は、彼らの親ノードが接続点の重みと息子であるので、左にマージされており、K * k個のポイントに合成層のノードを取ることです。ここで、「重み」を理解することができます:私は、ノード、私の下の一層のみであれば、k個のポイントの合計が、私の体重はすべての私の子ノードの数と表示されています。誰が組み合わさIおよび私の親ノードを生成することで、すべてのノードの次のサブ長さIが1加算される、すなわち:この操作では、全長が追加され、「私が組み込まれています」。私の体重* 1。
この質問は、これまでほとんどストローク明確なアイデアです:最初は徐々にポイントをマージし、適切なデータ構造に記憶された情報、軽量化のポイントを見つけて、その親ノードと高い(深い)の重みを更新し、各ステップを記録します最後の点に起因する全長、および高い総出力長頂点の効果。
データ構造は、ここで推奨 - プライオリティキューを。このアプリケーションは、非常に単純なもので、自身が使用するいくつかのブラインドを試してみてください。ここでのプライオリティキューは、同様の構造のヒープをシミュレートすることで、常にそのキューの前にいくつかの右の小さな点です。
最後に、大きなディテール。事態が発生した場合でも、上記の考えに基づいて行います。示すように、。。N- = 6、K = 3。
あなただけ上記のロジックは、このような状況で発生従事しなければならない従っている場合は、ポイントをマーク赤い円は数「2」だったかもしれない、それは接頭辞、他ではありませんが、それはこの位置に浪費されます。解決策:適切に完全にK-木になるために木にいくつかのより多くのポイントをキャッチするには、これらの点の重みは、あなたが上記を達成することができますので、確かにポイントスクイズされているだろう、一番下にランク付けされ、答えには影響を与えないことを(0であります)機能まで、高さは1に初期化されます。点の数は、K-1-(N-1)%(K-1)。徐々に〜感じるようになりました
動作点の後に補体(又はn = 6、K = 3)である:ここでの唯一のポイントを作る必要があります。私たちが作るポイントは、元と呼ばれている。例は確かにポイントの下に詰めすることが最初です。我々は赤い丸に行ったときだから実際には、6時の位置が充填された、と指摘。このポイントは、その後、一緒にマージし、青色の円のうち2点であろう。図:
だから、我々はそれがこの位置に赤い丸を押し置きます。
わかりました。コード付き。
書式#include <iostreamの> の#include <iomanip> 書式#include <アルゴリズム> 書式#include <CStringの> の#include <cstdioを> する#include <キュー> 使用して 名前空間はstdを、 int型N、K。 長い 長いレンス。 構造体ツリー{ 長い 長ヴァル。 長い 長い時間。 }。 PRIORITY_QUEUE <木> Q; // 优先队列 ブール 演算子 < (ツリーA、ツリーB){ 場合(!a.val = b.val)リターン a.val> b.val。 //右高いランキングに低い点から 他の 戻り AH> BH; // そうでない場合は(フルツリーkをシミュレートするために、より低い高さを優先 } インラインロング ロング MAX(ロング ロング A、ロング ロングBを){ IF(A> B)返す; それ以外の 戻りB; } int型のmain(){ CIN >> N- K; ロング ロング = REMAIN 0 ; のための(int型私は= 1 ; I <= N-Iは++ ){ ツリーA; scanfの("LLDの%」、A&.val); AH = 1 ; q.push(A); } int型エクストラ= 0 ; IF((N- 1)%( - K- 1))= - K-エクストラ1 - (N- 1。)%( - K- 1。); // *奥行き練習魂* のための(int型 I = 1 ;私は=余分<;私は++){ // 希望いっぱいのk追加のノードツリーを作る必要性 木。 .val = 0 ; AH = 1 ; q.push(A); } REMAIN= N + + エクストラ; 一方(REMAIN =!1。){ ツリーA、 ロング ロング CNT = 1、MAXH = 0、TMP = 0 ; 一方(CNT <= K){ // Kバイナリ A = q.top() ; // Aがツリーに今最初のチーム(最小量)である )(q.pop; MAXH = MAX(MAXH、AH); //はノード現在合成されている高さの更新 + = TMP a.valと、 CNT + + ; } AH = + MAXH 1。; a.val = TMP; レンス + = TMP; q.push(A)。 残る =まま-K + 1 。 } ツリーA = q.top()。 coutの <<レンス<<てendl <<ああ- 1 。 }
これは、最初の時間は非常に神経質...文書を発行しなければなりません。良い類似性(カーン)ことを見出し、問題解決羅バレーを見ました。。