Optimal Merge Pattern

2 minute read

Optimal Merge Pattern

문제 정의

이 문제는 k 개의 파일을 합치는 문제이다.
k 개의 파일을 합치는 방식은 여러가지가 있다.
n 개와 m 개의 레코드를 가진 정렬된 파일을 하나로 합치는데에 걸리는 시간은 O(n+m) 이다.

k 개의 정렬된 파일들을 서로 합치는데, 이때 가장 최적의 방법을 구하라.

예 :

X1, X2, X3 의 정렬된 파일이 있다. 이들의 길이는 각각 30, 20, 10이다.

이때 방법 1 은, X1 과 X2 로 Y1 생성 (50)
그리고 Y1 과 X3 로 Y2 최종 생성 (60)

방법 2는 X2와 X3 으로 Y1 (30)
Y1 과 X1 로 Y2, (60)

이들은 각 110, 90의 시간이 걸린다.

해결 방법

둘을 합치는 방법은 외부 경로(external path length)를 최소화 하는 이진 병합 트리이다.

merge

어떤 방법에서 드는 길이 qi 가 트리의 깊이 di 에 있을 때,
그 패턴을 옮기는데 드는 시간은 qi * di 이고,
총 걸리는 횟수는 1<=i<=n 까지 qi * di 의 합이다.

이는 heap 구조를 이용한다.
heap 에 합쳐야 할 파일들이 크기 순으로 정렬이 되어 있고,
두 개를 pop 하여 얻어진 것들을 노드의 왼쪽 오른쪽으로,
노드의 값(가중치)는 그 둘의 합으로 저장하고 그 값을 다시 heap 에 넣는다.
이를 heap 이 빌 때까지 반복한다. (혹은 n-1번)

그리고 이 시행이 끝난 후, root 에 있는 가중치가 바로 최적의 해이다.

코드

자료구조 :

class Tree {
public:
	Tree* left;
	Tree* right;
	int cost;
	void init(Tree* left, Tree* right, int cost) {
		this->left = left;
		this->right = right;
		this->cost = cost;
	}
};

struct compare {
	bool operator()(Tree*a, Tree* b) {
		return a->cost > b->cost;
	}
};

typedef priority_queue< Tree*, vector<Tree*>, compare> Heap;

입력 :

int input(Heap* files) {
	int n;
	int l;
	cin >> n;
	for (int i = 0; i < n; i++) {
		cin >> l;
		Tree* leaf = new Tree;
		leaf->init(NULL, NULL, l);
		files->push(leaf);
	}
	return n;
}

힙에서 pop 과 동시에 값 반환 :

Tree* withdraw(Heap* files) {
	Tree* len = files->top();
	files->pop();
	return len;
}

트리 만들기 :

void buildTree(int n, Heap* files, Tree* root) {
	for (int i = 0; i < n-1; i++) {
		Tree* tnode = new Tree;
		Tree* left = withdraw(files);
		Tree* right = withdraw(files);
		tnode->init(left, right, left->cost + right->cost);
		files->push(tnode);
		*root = *tnode;
	}
}

완성 코드 :

#include <iostream>
#include <queue>

using namespace std;

class Tree {
public:
	Tree* left;
	Tree* right;
	int cost;
	void init(Tree* left, Tree* right, int cost) {
		this->left = left;
		this->right = right;
		this->cost = cost;
	}
};

struct compare {
	bool operator()(Tree*a, Tree* b) {
		return a->cost > b->cost;
	}
};

typedef priority_queue< Tree*, vector<Tree*>, compare> Heap;

int input(Heap* files);
void buildTree(int n, Heap* files, Tree* root);

int main() {
	Heap files;
	Tree root;
	int n = input(&files);
	buildTree(n, &files, &root);
	cout << root.cost;
}

int input(Heap* files) {
	int n;
	int l;
	cin >> n;
	for (int i = 0; i < n; i++) {
		cin >> l;
		Tree* leaf = new Tree;
		leaf->init(NULL, NULL, l);
		files->push(leaf);
	}
	return n;
}

Tree* withdraw(Heap* files) {
	Tree* len = files->top();
	files->pop();
	return len;
}

void buildTree(int n, Heap* files, Tree* root) {
	for (int i = 0; i < n-1; i++) {
		Tree* tnode = new Tree;
		Tree* left = withdraw(files);
		Tree* right = withdraw(files);
		tnode->init(left, right, left->cost + right->cost);
		files->push(tnode);
		*root = *tnode;
	}
}

입력값

5
5
10
30
20
30

으로 95라는 결과를 얻을 수 있다.

Categories:

Updated: