7.1 그리디 알고리즘
- 그리디 알고리즘은 현재 상태에서 보는 선택지 중 최선의 선택지가 전체 선택지 중 최선의 선택지라고 가정하는 알고리즘
그리디 알고리즘의 핵심 이론
- 그리디 알고리즘은 다음과 같은 3단계를 반복하면서 문제를 해결한다.
그리디 알고리즘 수행 과정
1. 해 선택: 현재 상태에서 가장 최선이라고 생각되는 해를 선택
2. 적절성 검사: 현재 선택한 해가 전체 문제의 제약 조건에 벗어나지 않는지 검사한다.
3. 해 검사: 현재까지 선택한 해 집합이 전체 문제를 해결할 수 있는지 검사한다. 전체 문제를 해결하지 못한다면 1로 돌아가 같은 과정을 반복한다.
문제 035: 동전 개수의 최솟값 구하기
풀이
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
| import java.util.*;
import java.io.*;
public class Main {
public static int N, result;
public static long K, now, rest;
public static int[] A;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
K = Long.parseLong(st.nextToken());
A = new int[N];
now = 0;
rest = K;
result = 0;
for(int i=0; i<N; i++) {
A[i] = Integer.parseInt(br.readLine());
}
greedy(now, N-1, rest);
System.out.println(result);
}
static void greedy(long now, int idx, long rest) {
if (rest == 0) return;
if (idx < 0) return;
if (rest / A[idx] == 0) {
while (idx >= 0 && rest / A[idx] == 0) {
idx--;
}
greedy(now, idx, rest);
} else {
now += A[idx] * (rest / A[idx]);
result += rest / A[idx];
rest = rest - (A[idx] * (rest / A[idx]));
idx--;
greedy(now, idx, rest);
}
}
}
|
더 간단한 풀이 ••
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
| import java.util.Scanner;
public class Main
{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int N = sc.nextInt(); // 동전 "종류"의 수, 동전의 개수는 무한.
int K = sc.nextInt(); // 만드려고 하는 동전의 가치의 합
int[] A = new int[N]; // 각각의 동전의 가치 배열 (오름차순)
int result = 0; // K원을 만드는데 필요한 동전 개수의 최솟값
for(int i = 0; i < N; i++) {
A[i] = sc.nextInt();
}
for(int i = N - 1; i >= 0; i--) {
// 현재 동전의 가치가 K보다 작거나 같으면
if(A[i] <= K) {
result += (K / A[i]);
K = K % A[i];
}
}
System.out.println(result);
}
}
|
문제 036 : 카드 정렬하기
틀린 풀이
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
| import java.util.*;
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int N = Integer.parseInt(br.readLine());
int[] num = new int[N];
int result = 0;
for(int i=0; i<N; i++) {
num[i] = Integer.parseInt(br.readLine());
}
Arrays.sort(num);
if (N == 1) result = num[0];
if (N >= 2) result = num[0] + num[1];
if (N > 3) {
for(int i=2; i<N; i++) {
result = result + (result + num[i]);
}
}
System.out.println(result);
}
}
|
- 매번 가장 작은 두 묶음을 뽑아서 합치고, 그 합을 다시 묶음으로 넣어야 하는데 지금 코드는 다음에 합칠 2개를 고정해버림. → 합한 값에 따라 그때 그때 가장 작은 2개가 달라진다는 점 인지 못함
책에서의 풀이
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
| import java.util.*;
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int N = Integer.parseInt(br.readLine());
PriorityQueue<Integer> pq = new PriorityQueue<>();
for(int i=0; i<N; i++) {
int num = Integer.parseInt(br.readLine());
pq.add(num);
}
int data1=0;
int data2=0;
int sum=0;
while(pq.size() != 1) {
data1 = pq.remove();
data2 = pq.remove();
sum += data1+data2;
pq.add(data1+data2);
}
System.out.println(sum);
}
}
|
문제 037 : 수를 묶어서 최댓값 만들기
아이디어
- 수의 집합을 1보다 큰 수, 1, 0, 음수 이렇게 4가지 유형으로 나눠 저장한다.
- 1보다 큰 수의 집합을 정렬해 최댓값부터 차례대로 곱한 후에 더한다. 원소의 개수가 홀수일 때 마지막 남은 수는 그대로 더한다.
- 음수의 집합을 정렬해 최솟값부터 차례대로 곱한 후에 더한다. 원소의 개수가 홀수일 때 수열에 0이 있다면 1개 남는 음수를 0과 곱해 0을 만들고, 수열에 0이 없다면 그대로 더한다.
- 과정 2~3에서 구한 값을 더하고, 그 값에 숫자 1의 개수를 더한다.
풀이
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
48
49
50
51
52
53
54
| import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int N = Integer.parseInt(br.readLine()); // 수열의 크기
PriorityQueue<Integer> plusPq = new PriorityQueue<>(Collections.reverseOrder());
PriorityQueue<Integer> minusPq = new PriorityQueue<>();
int one = 0;
int zero = 0;
for(int i=0; i<N; i++) {
int data = Integer.parseInt(br.readLine());
if (data > 1) {
plusPq.add(data);
} else if (data == 1) {
one++;
} else if (data == 0) {
zero++;
} else {
minusPq.add(data);
}
}
int sum = 0;
// 양수 처리하기
while(plusPq.size() > 1) {
int first = plusPq.remove();
int second = plusPq.remove();
sum = sum + first * second;
}
if (!plusPq.isEmpty()) {
sum = sum + plusPq.remove();
}
// 음수 처리하기
while(minusPq.size() > 1) {
int first = minusPq.remove();
int second = minusPq.remove();
sum = sum + first * second;
}
if (!minusPq.isEmpty()) {
if (zero == 0) {
sum = sum + minusPq.remove();
}
}
// 1 처리하기
sum = sum + one;
System.out.println(sum);
}
}
|
문제 038 : 회의실 배정하기
풀이
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
48
49
| import java.util.*;
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int N = Integer.parseInt(br.readLine());
int[][] A = new int[N][2];
for(int i=0; i<N; i++) {
StringTokenizer st = new StringTokenizer(br.readLine());
A[i][0] = Integer.parseInt(st.nextToken());
A[i][1] = Integer.parseInt(st.nextToken());
}
Arrays.sort(A, new Comparator<int[]>(){
@Override
public int compare(int[] S, int[] E) {
if (S[1] == E[1]) {
return S[0] - E[0];
/*
첫번째 값(index 0) 기준 오름차순
- 음수: S가 앞
- 0: 동일
- 양수: E가 앞
*/
}
return S[1] - E[1];
/*
두번째 값(index 1) 기준 오름차순
- 음수: S가 앞
- 0: 동일
- 양수: E가 앞
*/
}
});
int count = 0;
int end = -1;
for (int i=0; i<N; i++) {
if(A[i][0] >= end) { // 겹치지 않는 다음 회의가 나온 경우
end = A[i][1]; // 종료시간 업데이트 하기
count++;
}
}
System.out.println(count);
}
}
|
- 겹치지 않게 최대한 많은 회의를 배정해야 한다. → 회의의 종료시간이 빠를 수록 유리하다.
- 종료 시간이 빠른 순서대로 정렬해 겹치지 않는 회의를 적절하게 선택하면 문제 해결
- 종료시간이 같으면 시작 시간이 빠른 순서로 정렬하는 로직도 추가.
- (2,2)가 먼저 등장하면, (1,2)가 불가능해지는 문제 방지
문제 039 : 최솟값을 만드는 괄호 배치 찾기
풀이
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
| import java.util.*;
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String ex = br.readLine();
String[] str = ex.split("-");
int answer = 0;
for(int i=0; i < str.length; i++) {
int temp = mySum(str[i]);
if (i==0) answer = answer + temp; // 가장 앞에 있는 값만 더함
else answer = answer - temp; // 뒷부분은 더한 값들을 뺌
}
System.out.println(answer);
}
static int mySum(String a) {
int sum = 0;
String temp[] = a.split("[+]");
for(int i=0; i<temp.length; i++) {
sum += Integer.parseInt(temp[i]);
}
return sum;
}
}
|