그리디 알고리즘을 활용할 수 있는 문제에서 브루트포스로 풀다가 시간초과가 났다.
나름 정렬을 했다고 생각했는데, 정렬 기준을 잘못 잡았다는 게 문제였다.
그리고 활동 선택 문제라는 분류가 그리디 알고리즘의 대표적인 문제임을 알고 포스팅을 작성해보기로 한다.
탐욕 알고리즘(Greedy algorithm)은 최적해를 구하는 데에 사용되는 근사적인 방법으로, 여러 경우 중 하나를 결정해야 할 때마다 그 순간에 최적이라고 생각되는 것을 선택해 나가는 방식으로 진행하여 최종적인 해답에 도달한다.
활동 선택 문제에서 그리디하게 푼다는 것.
= 가능한 활동시간 내에서 최대한 많은 활동을 할 수 있도록 활동들의 집합을 고르는 것.
시작시간과 종료시간으로 표시된 일련의 활동이 주어지면, 주어진 시간 내에 수행할 충돌하지 않는 활동을 선택하는 것과 관련된 조합 최적화 문제.
이런 유형은 그리디 알고리즘을 사용해 솔루션을 찾으면 항상 최적의 솔루션이 나온다.
제일 먼저 끝나는 활동을 선택했을 때, 남은 subproblem에서 최대한 많은 활동을 고를 수 있다.
java로 풀었다.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Comparator;
import java.util.StringTokenizer;
public class Main {
static class Time {
int start;
int end;
Time(int start, int end) {
this.start = start;
this.end = end;
}
int getStart() {
return start;
}
int getEnd() {
return end;
}
}
static int N;
static Time[] times;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st;
N = Integer.parseInt(br.readLine());
times = new Time[N];
for (int i = 0; i < N; i++) {
st = new StringTokenizer(br.readLine());
times[i] = new Time(Integer.parseInt(st.nextToken()), Integer.parseInt(st.nextToken()));
}
Arrays.sort(times, Comparator.comparing(Time::getEnd)
.thenComparing(Time::getStart));
solve(0, 0, 0);
}
private static void solve(int cnt, int idx, int endTime) {
if(endTime >= times[times.length-1].end) {
if(idx < times.length && times[idx].start == times[idx].end) cnt++;
System.out.println(cnt);
System.exit(0);
}
for (int i = idx; i < N; i++) {
if(endTime <= times[i].start) {
solve(cnt+1, idx+1, times[i].end);
}
}
}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Comparator;
import java.util.StringTokenizer;
public class Main {
static class Time {
int start;
int end;
Time(int start, int end) {
this.start = start;
this.end = end;
}
int getStart() {
return start;
}
int getEnd() {
return end;
}
}
static int N, ans;
static Time[] times;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st;
N = Integer.parseInt(br.readLine());
times = new Time[N];
for (int i = 0; i < N; i++) {
st = new StringTokenizer(br.readLine());
times[i] = new Time(Integer.parseInt(st.nextToken()), Integer.parseInt(st.nextToken()));
}
/*
끝나는 시각을 빠른 순으로 정렬해준다.
단, 끝나는 시각이 동일할 경우 시작 시각이 빠른 순으로 정렬한다.
그 후에는 endTime을 정해 시작 시간이 그 이후일 경우를 세어주며 갱신한다.
*/
Arrays.sort(times, Comparator.comparing(Time::getEnd)
.thenComparing(Time::getStart));
int endTime = 0, cnt = 0;
for (int i = 0; i < N; i++) {
if(endTime <= times[i].start) {
endTime = times[i].end;
cnt++;
}
}
System.out.println(cnt);
}
}
아마 클래스를 따로 만들지 않고 2차원배열로 풀었으면 메모리가 더 적었을듯.
그리디 알고리즘.. 어렵다.
탐욕 이후의 선택이 기존의 결과에 영향을 미치지 않는 독립적인 사건들이다?
-> 그리디로 최적해를 구해보자!
https://en.wikipedia.org/wiki/Activity_selection_problem
https://st-lab.tistory.com/145
https://doorbw.tistory.com/75