특정 시작 지점에서 다른 모든 지점까지의 거리를 구한다
이 알고리즘에는 현재까지 최단거리가 확정된 노드들의 집합인 Y, 현재까지 계산된 시작 노드로부터의 최단거리를 나타내는 배열인 length가 사용된다. length는 최초에는 시작 노드에서의 거리를 단편적으로 저장하는데, Y 집합에 노드가 추가됨에 따라, "시작 노드에서 -> Y에 속한 노드를 경유하여 -> 각 노드로 가는 최단거리" 를 저장하게 된다.
무슨 말인지, 아래를 읽어보면서 조금씩 더 알아보자.
length 배열에서의 vnear까지의 거리 + vnear에서 각 노드까지의 거리가 원래 length 배열에서의 i까지의 거리보다 작은 경우, 해당 값으로 length를 갱신한다.
(기존의 Y에서 해당 노드로 가는 방법보다, 새로 추가된 vnear를 거쳐 가는 쪽이 더 가까운지 확인하는 과정이다)
결론 : v1부터 각 노드까지의 거리 -> [0, 5, 4, 2, 1]
허프만 코드는 Prefix 속성을 가지는 코드이다. 이때 Prefix 속성이란, 한 문자의 코드가 다른 문자의 코드의 앞부분이 될 수 없는 성질을 의미한다. 이러한 성질이 필요한 이유는, 어떠한 코드 문자열이 Prefix 속성을 만족해야만 유일한 해석이 존재한다. 코드가 Prefix 속성을 가지지 않는 경우, 같은 코드에 대해 여러 다른 해석이 존재할 수 있게 된다.
이때 어떠한 문자를 효율적으로 코드로 변환하기 위해서는, 코드에서 여러 번 사용되는 문자는 최대한 짧게, 가끔 사용되는 문자에 대해서는 긴 코드를 할당해야 할 것이다, 허프만 코드란, 주어진 조건 하에서 코드의 총 길이는 최대한 짧게, 그러면서도 Prefix 속성을 만족하는 Binary 코드를 말한다.
이러한 과정을 반복하면, 위의 사진과 같은 모양이 만들어지게 된다.
아주 단순한데, "왼쪽 자식으로 가면 0, 오른쪽 자식으로 가면 1을 부여한다".
즉 위의 그림에서 e는 Root 기준으로 5번 오른쪽으로 이동하면 되므로 11111이고, d는 왼쪽으로 한 번, 오른쪽으로 한 번 이동하면 되므로 01이 된다.
물건마다 특정한 무게와 가격을 가지고 있다고 하자. 또한 우리의 배낭은 물건을 채울 수 있는 최대 무게가 정해져 있다. 이때 목표는 다음과 같다.
배낭의 최대 한도 안에서, 가장 높은 가격을 가지도록 물건을 채우자 (단, 하나의 물건을 나누어서 넣을 수도 있다)
물건을 부분적으로 나누어 넣을 수 있다는 조건이 존재하는 경우, 이 문제를 greedy 알고리즘으로 해결할 수 있다는 사실이 알려져 있다. (이러한 조건이 없는 경우에는, Backtracking 혹은 Branch-and-Bound 알고리즘을 이용한다)
예를 들어 보자. 배낭이 30kg까지 담을 수 있다고 할 때,
item1 : 5kg / 50만원
item2 : 10kg / 60만원
item3 : 20kg / 140만원
이라고 가정하자. 배낭에 가장 높은 가격의 물건을 담기 위해서는 우선 단위 무게당 값어치가 높은 물건부터 최대한 넣고, 더 넣을 수 없는 경우 남은 무게만큼 잘라서 넣으면 된다.
따라서 단위 무게당 가치가 가장 높은 item1을 넣고, 다음으로 item3를 넣은 후, 남은 5kg에 물건을 넣기 위해 item2를 절반 잘라 넣으면 된다. 이렇게 시행하는 경우 총 220만원어치를 넣을 수 있게 된다.
물건마다 특정한 무게와 가격을 가지고 있다고 하자. 또한 우리의 배낭은 물건을 채울 수 있는 최대 무게가 정해져 있다. 이때 목표는 다음과 같다.
배낭의 최대 한도 안에서, 가장 높은 가격을 가지도록 물건을 채우자 (단, 하나의 물건을 나누어서 넣을 수는 없다)
이 알고리즘의 풀이 방법을 생각하기 전에, 우선 조금 특수한 형태의 Knapsack Problem으로 감을 잡아 보자.
물건마다 특정한 무게와 가격을 가지고 있다고 하자. 또한 우리의 배낭은 물건을 채울 수 있는 최대 무게가 정해져 있다. 이때 목표는 다음과 같다.
배낭의 최대 한도 안에서, 가장 높은 가격을 가지도록 물건을 채우자 (단, 하나의 물건을 나누어서 넣을 수도 있다)
물건을 부분적으로 나누어 넣을 수 있다는 조건이 존재하는 경우, 이 문제를 greedy 알고리즘으로 해결할 수 있다는 사실이 알려져 있다. (이러한 조건이 없는 경우에는, Backtracking 혹은 Branch-and-Bound 알고리즘을 이용한다)
예를 들어 보자. 배낭이 30kg까지 담을 수 있다고 할 때,
item1 : 5kg / 50만원
item2 : 10kg / 60만원
item3 : 20kg / 140만원
이라고 가정하자. 배낭에 가장 높은 가격의 물건을 담기 위해서는 우선 단위 무게당 값어치가 높은 물건부터 최대한 넣고, 더 넣을 수 없는 경우 남은 무게만큼 잘라서 넣으면 된다.
따라서 단위 무게당 가치가 가장 높은 item1을 넣고, 다음으로 item3를 넣은 후, 남은 5kg에 물건을 넣기 위해 item2를 절반 잘라 넣으면 된다. 이렇게 시행하는 경우 총 220만원어치를 넣을 수 있게 된다.
물건을 나누어서 담을 수 없는 경우, greedy 알고리즘으로 문제를 해결할 수 없다. 이 경우에는 동적계획법에 해당하는 방법으로 문제를 해결하게 된다. 동적계획법에 기반하므로 기본적으로 재귀 구조를 사용하게 된다.
우선 용어를 다음과 같이 정의하자
P[n][W] : n번째 물건까지를 이용하여, W 한도 내에서 채울 수 있는 최대 가격
이때 P[n][W]를 P[n-1][~]을 통해 계산한다고 생각해 보자. P[n-1][~]의 값은 이미 알고 있다고 가정한다. 이 값들을 기반으로 P[n][W]을 계산한다고 치면, 우리는 가장 처음으로 이러한 생각을 하게 되지 않을까?
n번째 물건을 포함하는 쪽이 맞을까, 아니면 포함하지 않는 쪽이 맞을까?
따라서 P[n][W]는, 아래의 두 가지 중 더 큰 쪽이 된다.
1. n번째 물건을 포함하지 않는 경우, 즉 n-1번째 물건까지만을 사용하여 W 한도로 배낭을 채우는 경우 : P[n-1][W]
2. n번째 물건을 포함하는 경우. 즉 n번째 물건을 무조건 포함하고, 나머지 n-1 번째 물건까지를 이용하여 남은 만큼의 무게를 채우는 경우를 의미한다. : n번째 물건의 가격 + P[n-1][W-wn] (wn은 n번째 물건의 무게를 의미한다).
위의 관계식을 이용하여 P[n][W]를 추측하면 되겠다.
P[2][5]를 구하는 과정을 보자. P[2][5]는 P[1][5] (= 10)와, 40 + P[1][1]중 큰 값으로 결정된다. 이때 P[1][1]은 0임이 자명하므로 (한도가 1kg 이내인 물건은 없다), 2번 물건을 포함하는 쪽이 최적이 된다.
다음으로는 P[4][10]을 구하는 과정을 보자. 과정은 다음과 같다.
1. P[4][10] = Max(P[3][10], 50 + P[3][7]) = Max(70, 50 + 30) = 80
2. P[3][10] = Max(P[2][10], 30 + P[2][4]) = Max(50, 30 + 40) = 70
3. P[3][7] = Max(P[2][7], 30 + P[2][1]) = Max(40, 30 + 0) = 40
4. P[2][10] : item1, item2를 넣는 쪽이 최적임이 자명하므로, 10 + 40 = 50이다
5. P[2][4] : item2만 넣는 쪽이 최적임이 자명하므로, 40이다.
6. P[2][7] : item2만 넣는 쪽이 최적임이 자명하므로, 40이다.
7. P[2][1] : 아무것도 넣을 수 없으므로, 0이다.