「アルゴリズムノート」学習日記-9.7ヒープ

9.7ヒープ

コードアップコンテストID:100000616

問題A:アルゴリズム10-10、10-11:ヒープソート

タイトル説明
ヒープの並べ替えは、ヒープ構造を使用する並べ替え方法です。レコードサイズの補助スペースが1つだけ必要で、並べ替える各レコードは1つのストレージスペースを使用するだけで済みます。
最初に小さなルートヒープまたは大きなルートヒープを構築し、次にヒープの性質を使用します。つまり、各要素の位置を順番に取得するために、ヒープの最上部にある要素が最小または最大になります。
ヒープの並べ替えのアルゴリズムは次のように説明できます。
ここに画像の説明を挿入
この質問では、整数の文字列を読み取り、上記のヒープの並べ替え方法を使用して小さいものから大きいものに並べ替えて出力します。
入力入力
の最初の行には、1つの正の整数nが含まれています。これは、ソートする必要がある整数が合計n個あることを示しています。ここで、nは100,000を超えません。
2行目には、スペースで区切られたn個の正の整数が含まれ、n個の整数がソートされることを示しています。
出力は
1行だけで、n個の整数を含み、すべての整数が小さいものから大きいものにソートされていることを示しています。
各整数の後にスペースを出力し、行末の改行に注意してください。
入力例

10
2 8 4 6 1 10 7 3 5 9

出力例

1 2 3 4 5 6 7 8 9 10 

思考の
デフォルトによって確立された))私はmake_heapで<アルゴリズム>ヘッダファイルを(直接この問題を(組み込みヒープ)とsort_heap()(ソート・ヒープ)、(make_heap、ということに注意すべきことは、大きなトップヒープで、利用sort_heap ()その後、ヒープの性質は失われます。

範囲は、ヒープとしてのプロパティを失います。-《 C ++リファレンス》

コード

#include<cstdio>
#include<math.h>
#include<string.h>
#include<algorithm>
#include<vector>
#include<iostream>
using namespace std;
vector<int> Heap;
int main(){
	int n;
	while(scanf("%d", &n) != EOF){
		for(int i=1;i<=n;i++){
			int tmp;
			scanf("%d", &tmp);
			Heap.push_back(tmp);
		}
		make_heap(Heap.begin(), Heap.end());
		sort_heap(Heap.begin(), Heap.end());
		for(int i=0;i<Heap.size();i++) printf("%d ", Heap[i]);
		printf("\n");
		Heap.clear();
	}
	return 0;
}

質問B:シーケンスのマージ

タイトルの説明
長さNのシーケンスAとBが2つあります。AとBに数値を追加してN 2の合計を取得します。これらのN 2の合計の最小Nを見つけます。
入力した
最初の行を正の整数Nを(1 <= N <= 100000)。
Nの整数Aの第2行I、満足A I <= A I + +1及びA I <= 109
Nの整数Bの第3行I、満足B I <B = I + +1とB I <= 109
出力
の出力N個の整数を含む行が1行だけあります。最小のN個の合計は、隣接する数値がスペースで区切られて、小さいものから大きいものへと出力されます。
入力例

3
2 6 6
1 4 8

出力例

3 6 7

ヒント
最小ヒープを使用することをお勧めします。
アイデア
この質問は、質問機について奇妙に感じます。必要な制限時間は1秒、つまり1000ミリ秒ですが、2000ミリ秒を超えるとACになることもあります。最も奇妙なのは、これを判断ステートメントに書き込むと、タイムアウトになる(5000ミリ秒を超える)ことです。

int sum = seqA[i]+seqB[j];
if(sum<q.top()){//如果比队首元素更小
    q.pop(); //弹出原先的队首元素
    q.push(sum);//放入优先队列里 
}

そして、これはACです。

if(seqA[i]+seqB[j]<q.top()){//如果比队首元素更小
    q.pop(); //弹出原先的队首元素
    q.push(seqA[i]+seqB[j]);//放入优先队列里 
}

オンラインで検索した後、ローカル変数の宣言によって時間がかかるようになります(ただし、誇張されすぎてそれほど悪くはありません...)。一方、グローバル変数と静的変数はそうではありません。

また、この質問では、ヒープを使用してタイムアウトなしで直接書き込む方法を考えることはできません(ビッグブラザーメソッドを使用してサイズを判断するアルゴリズムを最適化した場合でも、ヒープのソートの問題があります。ビッグトップヒープでもルートノードしか保証できないためです。値は左右の子よりも大きいですが、左右の子が順序付けされていることを保証するものではないため、処理後のヒープでは、直接の回答ではありませんが、ヒープでソートする必要がありますが、この操作は確実にタイムアウトします...)そして、インターネット上の大手のコードを参照すると、それらはすべて優先キューで作成されていることがわかりましたが、優先キューの下部がヒープであるため、優先キューもヒープ操作と見なす必要があります...

この質問のアルゴリズムはより賢いです。簡単に言えば、[ ゆっくりと雨に空を浸す ] を参照して、Aシーケンスの最初の数をBシーケンスのすべての数に順番に追加してから、ストレージNを取得します。合計のプライオリティキュー(ここでのプライオリティキューはデフォルト、つまり最大数はキューの先頭にあります)、その後、シーケンスAの2番目の数にシーケンスBのすべての数を順番に追加させ、追加するかどうかを判断します合計がチームの最初の要素よりも小さい(できるだけ小さいキューを取得したいので、大きな数を排除する必要があります)。次に、チームの最初の要素がチームを離れ、その数がチームに入るようにします。重要な点は、追加された合計がヘッド要素以上である場合、直接ブレークすることです(ここでは、両方のシーケンスの特性を非減少シーケンスとして使用し、現在の合計がすでに大きい場合はループバックします)大きくなるだけなので、この文を追加すると時間がかかる可能性があります)。

最後の処理の後、優先順位キューは大から小に並べられます。キューにN個の番号が必要であり、それが後方に配列に割り当てられている限り、この配列が答えになります(私はスタックの特性を使用して保存しても、結果はタイムアウトになります...)。
コード

#include<cstdio>
#include<math.h>
#include<string.h>
#include<algorithm>
#include<queue>
#include<iostream>
using namespace std;
const int maxn = 100001;
priority_queue<int> q;
int seqA[maxn]={0};
int seqB[maxn]={0};
int ans[maxn]={0};//不要用stack存储,会超时 
int main(){
	int N;
	while(scanf("%d", &N) != EOF){
		int len = 0;
		for(int i=0;i<N;i++) scanf("%d", &seqA[i]);
		for(int i=0;i<N;i++){
			scanf("%d", &seqB[i]);
			q.push(seqA[0]+seqB[i]);//将序列A的第一个元素与序列B的每个元素相加,得到的数入队 
		}
		for(int i=1;i<N;i++){
			for(int j=0;j<N;j++){
				if(seqA[i]+seqB[j]<q.top()){//如果比队首元素更小
					q.pop(); //弹出原先的队首元素
					q.push(seqA[i]+seqB[j]);//放入优先队列里 
				}
				else break;//因为都是非递减序列,如果当前不满足<q.top(),后面更大的数自然也不满足 
			}
		}
		for(int i=N-1;i>=0;i--){
			ans[i] = q.top();
			q.pop();
		}
		for(int i=0;i<N;i++){
			if(i==0) printf("%d", ans[i]);
			else printf(" %d", ans[i]);
		}
		printf("\n");
		memset(seqA, 0, sizeof(seqA));
		memset(seqB, 0, sizeof(seqB));
		memset(ans, 0, sizeof(ans));
	}
	return 0;
}

質問C:果物(ヒープ)のマージ

トピックの説明
果樹園では、Duoduoはすでにすべての果物を倒し、果物の種類に応じてさまざまな山に分けました。Duoduoはすべての果物を山積みにすることにしました。
マージするたびに、2つの山の果物を組み合わせることができ、消費されるエネルギーは2つの山の重量の合計に等しくなります。すべての果物がn-1回マージされた後、山が1つだけ残っていることがわかります。果物をマージするときに消費される物理エネルギーの合計は、各マージで消費される物理エネルギーの合計に等しくなります。
これらの果物を家に戻すには多大な労力が必要になるため、果物をマージするときは、できるだけエネルギーを節約する必要があります。各果物の重みが1であり、果物の種類の数と果物の各種類の数がわかっていると仮定すると、あなたの仕事は、肉体的な労力の量を最小限に抑え、この最小限の肉体的労力の値を出力するように組み合わせシーケンススキームを設計することです。
例えば、果物は3種類あり、順番は1、2、9の順です。最初に1番目と2番目のヒープをマージできます。新しいヒープの数は3で、物理的な労力は3です。次に、新しいヒープが元の3番目のヒープとマージされ、新しいヒープが取得されました。数値は12、物理コストは12です。したがって、多くの肉体的な努力= 3 + 12 = 15。15が最小の物理コストであることを証明できます。
入力
入力ファイルfruit.inは2行で構成され、最初の行は整数n(1 <= n <= 30000)で、果物の数を示します。2行目には、スペースで区切られたn個の整数が含まれています。i番目の整数ai(1 <= ai <= 20000)は、i番目の果物の数です。
出力
出力ファイルfruit.outには行が含まれています。この行には整数のみが含まれています。これは最小の物理的コストです。入力データは、この値が231未満であることを保証します。
入力例

10
3 5 1 7 6 4 2 5 4 1

出力例

120

ヒント
アップローダー:Lv Hongboの
アイデア
は前の質問から発想を得ています。この質問はまだ比較的高速であり、優先度キューが直接使用されます(ここでは、優先度は小さい数に設定されているため、宣言するときにより大きい<int>を書き込む必要があります。 make_heap()関数は、同じ方法で小さなトップヒープを形成します)、次に2つの小さな数値num1、num2がデキューされるたびに、それらを合計電力消費量の合計(sum + = num1 + num2)に加算し、次にそれらの合計を再入力します(この操作は、2つの小さなヒープが1つの大きなヒープにマージされるため、キューに入れる必要があるためです)。
ループの終了条件については、forループを使用してfor(int i = 1; i <= n-1; i ++)を書き込むことができます(タイトルには、n-1のマージ後、ヒープが1つだけ残っていることも示されているため)、 while(q.size()> 1)(キュー内の各要素はヒープであるため、q.size()== 1の場合、q.size()である限り、フルーツヒープが1つだけ残っていることを意味します) > 1は、2つ以上の果物の山があり、ループを実行する必要があることを示します。
コード

#include<cstdio>
#include<math.h>
#include<string.h>
#include<algorithm>
#include<queue>
#include<iostream>
using namespace std;
priority_queue<int, vector<int>, greater<int> > q;
int main(){
	int n;
	while(scanf("%d", &n) != EOF){
		for(int i=1;i<=n;i++){
			int tmp;
			scanf("%d", &tmp);
			q.push(tmp);
		}
		int sum = 0;
		while(q.size()>1){//因为队列里放的是果子堆,目标是合成一个大堆,所以当q.size()==1时停止循环 
			int num1 = q.top();
			q.pop();
			int num2 = q.top();
			q.pop();
			sum += num1+num2;//先把两个小的果子堆合并
			q.push(num1+num2);//再把它入队,表明这是一个合并好的大堆 
		}
		printf("%d\n", sum);
		q.pop();//把最后一个堆出队
	}
	return 0;
}

まとめ

そのような問題を解決するために優先キューを使用することは非常に便利ですが、質問をするときの最初の選択は、間違いなく優先キューを使用することです(他の人が作ったホイールを使用)。このことから、STLのプライオリティキューは<algorithm>のmake_heap()よりもはるかに便利であり、問​​題解決の論理的側面に注意を集中できることがわかります。ただし、重要なデータ構造として、ヒープはツリーのブランチであり(ヒープは完全なバイナリツリーです)、その実装を理解する必要があります。ホイールを作らなくても、少なくともこれを有効にする必要があります。ホイールをお楽しみください。

元の記事を54件公開 賞賛された27件 訪問4973件

おすすめ

転載: blog.csdn.net/weixin_42257812/article/details/105587303