2021. 1. 18. 20:10ㆍ알고리즘/알고리즘 문제 [easy]
1. 탐욕법
문자들을 합쳐 문자열을 만드는데 드는 비용이 최소가 되게 선택하려면
일단 가장 작은 문자 두개를 선택해 합쳐나가야 할 것 같다.
합쳐진 문자열에 또 합치게 되면
이전에 합치게한 문자열이 중복으로 또 다시 카운트 되기 때문이다.
2. 증명
탐욕적 선택 속성을 증명하기 위해 먼저 가장 작은 두 문자가 서로 합쳐지지 않은 최적해가 있다고 해보자
a와 b가 가장 작은 두 수 일때 먼저 다른 문자열과 합친것을 다음과 같이 표현했다.
X = b + B
Y = a + C
거리가 같은경우 : X + Y 일 경우 a 와 B를 바꾸어도 비용은 일정하므로 답은 변하지 않는다
거리가 다른 경우: X + a 일경우 a와 B를 바꾸게 될경우 B가 병합되는 횟수가 적어져 비용이 줄어들게 된다.
그러므로 작은 두 문자열을 합치게 되면 비용은 항상 같거나 항상 줄어들게 된다.
작은 두수를 선택하도록 최적해를 바꾸는것으로 인해 총 비용은 더 커지지 않기 때문에
가장 작은 두 수를 선택하는 최적해는 반드시 존재하게 된다.
부분 문제들 또한 합친것을 포함한 남은 문자열 중에서 가장 작은 두개를 선택하는것이 이득이므로
최적 부분 구조는 자명하게 성립된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
#include <iostream>
#include <cstring>
#include <vector>
#include <utility>
#include <algorithm>
std::vector<int> str;
int joint()
{
int sum = 0;
while(str.size() != 1)
{
int a = -str.back();
str.pop_back();
int b = -str.back();
str.pop_back();
str.push_back(-(a + b));
std::sort(str.begin(), str.end());
sum = sum + a + b;
}
return sum;
}
int main()
{
int testCase = 0;
int N = 0;
std::cin>>testCase;
for (int i = 0; i < testCase; i++)
{
str.clear();
std::cin>>N;
for(int j = 0 ; j < N ; j++)
{
int tmp;
std::cin>>tmp;
str.push_back(-tmp);
}
std::sort(str.begin(), str.end());
std::cout<<joint()<<"\n";
}
}
|
cs |
3. 허프만 코드
이 문제는 탐욕적 알고리즘의 유명한 예인 허프만 코드 알고리즘을 각색한 것이다.
허프만 코드는 가변 길이 인코딩 테이블을 만드는 방법으로 여러 압축 알고리즘에서 사용된다.
가변 길이 인코딩이란 원문의 각 글자를 서로 길이가 다를 수 있는 비트 패턴으로 바꿈으로써
원문의 길이를 줄이는 방법이다.
자주출현하는 글자는 더 짧은 패턴으로, 가끔만 출현하는 글자는 더 긴 패턴으로 배당할 필요가 있다.
허프만 코드는 원문에 출현하는 글자의 출현 빈도가 주어질 때 예상 길이를 최소화하는 비트 패턴을 만들어준다.
각 글자에 대해 출현확률과 코드의 길이를 곱한 것의 합을 최소화 하는 것이다.
출현 확률이 문자열 길이로 바뀌었을 뿐 사실 위 문제와 똑같다.
'알고리즘 > 알고리즘 문제 [easy]' 카테고리의 다른 글
[계산 기하] PINBALL 핀볼 시뮬레이션. (0) | 2021.01.26 |
---|---|
[정수론] PASS486 비밀번호 456 (0) | 2021.01.24 |
[탐욕법] LUNCHBOX 도시락 데우기 (0) | 2021.01.18 |
[동적 계획법] DRAGON 드래곤 커브 (0) | 2021.01.15 |
[동적 계획법] PACKING 여행짐 싸기 (0) | 2021.01.13 |