반응형
그리디 알고리즘(Greedy Algorithm)
1. 개요
그리디 알고리즘(Greedy Algorithm)이란 현재 단계에서 가장 최적의 선택을 반복하여 문제를 해결하는 알고리즘입니다. 탐욕법이라고도 불리는 이 방식은 매 순간 최선의 선택이 결국 전체 문제의 최적해로 이어진다고 가정합니다.
이 접근법이 성공적으로 작동하려면 문제에 대한 몇 가지 조건이 만족되어야 합니다. 그렇지 않으면 전역 최적해(Global Optimal Solution)를 보장하지 못할 수 있습니다.
2. 그리디 알고리즘의 조건
- Greedy 선택 속성 (Greedy Choice Property)
- 각 단계에서의 선택이 이후 단계에 영향을 주지 않고, 그 순간 최적의 선택을 하면 전체 최적해에 도달할 수 있는 속성을 의미합니다.
- 최적 부분 구조 (Optimal Substructure)
- 문제를 작은 부분 문제로 나누었을 때, 각 부분 문제의 최적해들이 모여 전체 문제의 최적해를 구성할 수 있는 구조입니다.
3. 그리디 알고리즘의 장단점
장점
- 단순성: 각 단계에서 바로 최적의 선택을 하기 때문에 설계와 구현이 비교적 단순합니다.
- 속도: 불필요한 탐색을 줄여 빠른 실행 시간을 보장합니다.
단점
- 최적해 보장 불가: 모든 문제에서 항상 최적해를 보장하지는 않습니다.
- 문제 분석 필요: 그리디 알고리즘이 적용 가능한 문제인지 미리 판단해야 합니다.
4. 동작 원리
- 문제 정의: 전체 문제를 부분 문제들로 나눕니다.
- 탐욕적 선택: 현재 상황에서 최선의 선택을 합니다.
- 상태 갱신: 선택에 따라 문제의 상태를 업데이트합니다.
- 반복: 문제를 완전히 해결할 때까지 위 과정을 반복합니다.
5. 예제 문제
예시 1: 동전 거스름돈 문제
문제 설명
500원, 100원, 50원, 10원의 동전이 있을 때, 특정 금액을 거슬러 주기 위해 필요한 동전의 개수를 최소화하세요.
(단, 동전 종류가 항상 큰 단위의 배수로 주어짐)
해결 방법
- 동전을 내림차순으로 정렬한 후, 남은 금액을 가장 큰 동전으로 최대한 많이 나누는 방식으로 풀이합니다.
#include <stdio.h>
int greedy_change(int coins[], int size, int amount) {
int count = 0;
for (int i = 0; i < size; i++) {
if (amount == 0) break; // 남은 금액이 없으면 종료
count += amount / coins[i];
amount %= coins[i];
}
return count;
}
int main() {
int coins[] = {500, 100, 50, 10};
int amount;
printf("거슬러 줄 금액을 입력하세요: ");
scanf("%d", &amount);
int result = greedy_change(coins, 4, amount);
printf("최소 동전 개수: %d\n", result);
return 0;
}
입출력 예시
입력 출력
1260 | 최소 동전 개수: 6 |
과정 설명:
- 500원 동전 2개 → 남은 금액 260원
- 100원 동전 2개 → 남은 금액 60원
- 50원 동전 1개 → 남은 금액 10원
- 10원 동전 1개 → 남은 금액 0원
예시 2: 활동 선택 문제 (Activity Selection Problem)
문제 설명
여러 개의 활동이 주어졌을 때, 활동이 서로 겹치지 않으면서 최대한 많은 활동을 선택하세요.
각 활동은 시작 시간과 종료 시간으로 표현됩니다.
해결 방법
- 활동을 종료 시간 기준으로 정렬한 후, 가장 먼저 끝나는 활동부터 선택합니다.
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int start;
int end;
} Activity;
int compare(const void *a, const void *b) {
return ((Activity *)a)->end - ((Activity *)b)->end;
}
int activity_selection(Activity activities[], int size) {
qsort(activities, size, sizeof(Activity), compare);
int count = 1; // 첫 번째 활동 선택
int last_end = activities[0].end;
for (int i = 1; i < size; i++) {
if (activities[i].start >= last_end) {
count++;
last_end = activities[i].end;
}
}
return count;
}
int main() {
Activity activities[] = {{1, 3}, {2, 5}, {4, 6}, {6, 7}, {5, 9}, {8, 9}};
int size = sizeof(activities) / sizeof(activities[0]);
int result = activity_selection(activities, size);
printf("선택 가능한 최대 활동 개수: %d\n", result);
return 0;
}
입출력 예시
활동 목록 선택 가능 활동 수
(1, 3), (2, 5), (4, 6), (6, 7), (5, 9), (8, 9) | 4 |
과정 설명:
- 종료 시간이 빠른 순서로 정렬: (1, 3), (4, 6), (6, 7), (8, 9)
- 선택된 활동: (1, 3), (4, 6), (6, 7), (8, 9)
6. 한계와 고려 사항
- 문제 특성: 그리디 알고리즘이 적용 가능한 문제인지 확인해야 합니다.
- 최적해 불가 상황: 일부 문제에서는 그리디 방식이 최적해를 보장하지 못합니다. 예를 들어, 배낭 문제(Knapsack Problem)의 경우 동적 프로그래밍이 더 적합합니다.
7. 적용 가능한 문제 유형
- 최소 신장 트리(MST): 크루스칼, 프림 알고리즘
- 최단 경로: 다익스트라 알고리즘
- Huffman 코드 생성
- 거스름돈 문제 등
💡 도움이 되셨다면 댓글과 공감 부탁드립니다! 😊
📌 더 많은 알고리즘 풀이와 프로그래밍 자료는 블로그에서 확인하세요!
✉️ 문의나 피드백은 댓글이나 이메일로 남겨주세요.
반응형
'Computer Science > 자료구조 & 알고리즘' 카테고리의 다른 글
[자료구조 & 알고리즘] 해시 테이블(Hash Table) (0) | 2025.02.05 |
---|---|
[자료구조 & 알고리즘] 이분 탐색 (Binary Search) (0) | 2025.02.03 |
[자료구조 & 알고리즘] DP 추천 문제 및 유형 분류 (feat. 백준) (1) | 2025.01.30 |
[자료구조 & 알고리즘] 다이나믹 프로그래밍(Dynamic Programming) 이해하기 (0) | 2025.01.14 |
[알고리즘] 시뮬레이션 문제는 왜 어려울까? (feat. 설계의 중요성) (0) | 2025.01.09 |