그리디 알고리즘
정리
그리디 알고리즘(Greedy Algorithm), 직역하면 탐욕 알고리즘이다. 이름에서 직관적으로 알 수 있듯이, 매 선택에서 최선으로 보이는 답을 선택하는 알고리즘이다. 그만큼 빠르고 단순하지만 항상 최적해를 보장하지는 못한다.
조건
그리디 알고리즘을 적용시키기 위해서는 두 가지의 조건이 필요하다
탐욕 선택 속성: 앞의 선택이 이후의 선택에 영향을 주면 안된다. 현 시점에서의 최선의 선택이 전체적으로도 최선의 선택이어야 한다. 이 말이 완벽히 이해되지는 않는데, 가장 큰 예시인 활동 선택 문제를 떠올리면 받아들이기는 비교적 수월할 것 같다.
최적 부분 구조: 전체 문제의 최적해가 부분 문제의 최적해로 구성될 수 있어야 한다. 쉽게 말해 점화식이다. 해결하고자 하는 문제를 부분으로 나눈 작은 문제들을 반복했을 때 큰 문제에 도달할 수 있어야 한다.
그리디 알고리즘을 적용시키는 대표적인 예시는 아래와 같다.
회의실 배정
import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner;
import java.util.StringTokenizer;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int[][] times = new int[n][2];
for (int i = 0; i < n ; i++) {
times[i][0] = scanner.nextInt(); // 회의 시작 시간
times[i][1] = scanner.nextInt(); // 회의 종료 시간
}
// Arrays.sort를 이용한 이차원 배열 정렬
Arrays.sort(times, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
if (o1[1] == o2[1]) return o1[0] - o2[0]; // 종료 시간이 같으면 시작 시간이 작은 순서대로 정렬
return o1[1] - o2[1]; // 종료 시간이 작은 순서대로 정렬
}
});
for (int[] time : times) {
System.out.println(time[0] + " " + time[1]);
}
int answer = 0;
int endTime = 0;
for (int i = 0; i < n; i++) {
if (endTime <= times[i][0]) { // 시작 시간이 이전 회의의 끝나느 시간보다 크거나 같으면 갱신
endTime = times[i][1];
answer++;
}
}
System.out.println(answer);
}
}
한 블로그에서 이 문제를 풀 때 그려야 할 활동 시간들의 길이에 대한 그림을 제시해주었는데, 이것을 이해하면 문제를 쉽게 풀 수 있다. 이 문제는 활동 선택 문제라고 한다.
활동 선택 문제(Activity Selection Problem): 여러 활동이 있고, 작업 큐가 하나일 때, 최대한 많은 활동을 선택하는 것. 가장 먼저 끝나는 문제를 선택하고, 종료된 시간에 시작할 수 있는 가장 빠른 작업을 선택한다.
정렬하면 아래처럼 배열이 바뀌게 된다.
잃어버린 괄호
import java.util.Scanner;
import java.util.StringTokenizer;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
StringTokenizer st = new StringTokenizer(scanner.nextLine(), "-");
int answer = Integer.MAX_VALUE;
while(st.hasMoreTokens()) {
int temp = 0;
StringTokenizer tempSt = new StringTokenizer(st.nextToken(), "+");
while (tempSt.hasMoreTokens()) {
temp += Integer.parseInt(tempSt.nextToken());
}
if (answer == Integer.MAX_VALUE) {
answer = temp;
} else {
answer -= temp;
}
}
System.out.println(answer);
}
}
“-”를 기준으로 먼저 자르고, “+”를 기준으로 잘라 숫자들을 더해서 answer에 더해준다. “-”가 없는 경우도 찾아내기 위해 초기값을 Integer.MAX_VALUE로 해주고, 처음에만 더해주고 이후엔 뺄셈을 한다.
동전 0
import java.util.Scanner;
import java.util.StringTokenizer;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int k = scanner.nextInt();
int[] coins = new int[n];
for (int i = 0; i < n; i++) {
coins[i] = scanner.nextInt();
}
int answer = 0;
while (k != 0) {
if (coins[--n] <= k) {
answer += k/coins[n];
k = k%coins[n];
}
}
System.out.println(answer);
}
}
거스름돈 문제와 비슷한데, 가장 큰 수의 동전부터 k를 나누며 나아간다. 너무 간단한 문제라 설명은 생략..
'WINK-(Web & App) > 알고리즘 스터디' 카테고리의 다른 글
[2025 1학기 알고리즘 스터디] 박현빈 #2주차 (0) | 2025.04.09 |
---|---|
[2025 1학기 알고리즘 스터디] 김민재 #2주차 (0) | 2025.04.09 |
[2025 1학기 알고리즘 스터디] 김민주 #2주차 (0) | 2025.04.08 |
[2025 1학기 알고리즘 스터디] 이서영 #2주차 (0) | 2025.04.08 |
[2025 1학기 알고리즘 스터디] 남윤찬 #2주차 (0) | 2025.04.06 |