개요

그리디 알고리즘이란 현재 상황에서 지금 당장 좋은 것만 고르는 방법.

정당성 분석이 필수.

코딩 테스트에서는 탐욕법으로 얻은 해가 최적의 해가 되는 상황에서 이를 추론할 수 있어야 풀리도록 출제된다.

 

문제1. 거스름 돈

카운터에는 거스름돈으로 사용할 500원, 100원, 50원, 10원짜리 동전이 무한히 존재한다. 손님에게 거슬러 줄 돈이 N원일 때 거슬러 주어야 할 동전의 최소 개수를 구해라. 단, 거슬러 주어야 할 돈 N은 항상 10의 배수이다.

 

아이디어

가장 큰 화폐 단위부터 거슬러 주고, 다음으로 큰 화폐로 차례대로 거슬러 주면 최적의 해를 보장한다. 가지고 있는 동전 중 큰 단위가 항상 작은 단위의 배수이기 때문이다.

풀이:

#include <iostream>
using namespace std;

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);

	int n, cnt = 0;
	cin >> n;

	int coins[4] = { 500, 100, 50, 10 };
	for (int i = 0; i < 4; i++) {
		cnt += n / coins[i];
		n %= coins[i];
	}
	cout << cnt << "\n";
}

 

문제 2. 큰 수의 법칙

문제 

큰 수의 법칙이란 다양한 수로 이뤄진 배열이 있을 때 주어진 수들을 M번 더해 가장 큰 수를 만드는 법칙

단, 배열의 특정한 인덱스(번호)에 해당하는 수가 연속해서 K번을 초과할 수는 없음

 

입력 조건

첫째 줄에 N(2<=N<=1000), M(1<=M<=10000), K(1<=K<=10000)의 자연수가 주어지며 각 자연수는 공백으로 구분

둘째줄에 N개의 자연수가 주어짐. 각 자연수는 공백으로 구분. 단, 각각의 자연수는 1이상 10000이하의 수로 주어짐

입력으로 주어지는 K는 항상 M보다 작거나 같다.

 

출력 조건

첫째 줄에 큰 수의 법칙에 따라 더해진 답을 출력

 

입력 예시

5 8 3

2 4 5 4 6 

 

출력 예시

46 

(* 6 + 6 + 6 + 5 + 6 + 6 + 6 + 5  =  46)

 

아이디어 및 답지

// 3-2. 큰 수의 법칙
#include <iostream>
#include <algorithm>
using namespace std;

int n, m, k;
// 배열의 크기 n, 숫자가 더해지는 횟수 m, 연속해서 더해질 수 없는 횟수 k
int arr[1000] = {};
int main()
{
	cin >> n >> m >> k;

	for (int i = 0; i < n; i++) {
		cin >> arr[i];
	}

	// 2 4 5 4 6 // 6 6 6 5, 6 6 6 5 // n = 5, m = 8, k = 3
	// 3 4 3 4 3 // 4 4 4, 4 4 4, 4 // n = 5, m = 7, k = 2

	sort(arr, &arr[n]);
	int max_first = arr[n - 1];
	int max_second = arr[n - 2];

	int max_first_cnt = int(m / (k + 1)) * k + (m % (k + 1));
	int max_second_cnt = m - max_first_cnt; // **** 
	
	int result = 0;
	for (int i = 0; i < max_first_cnt; i++) {
		result += max_first;
	}
	for (int i = 0; i < max_second_cnt; i++) {
		result += max_second;
	}

	cout << result << "\n";
}

 

피드백

처음 풀 때는 아예 손도 못 대서 답지 보고 아이디어 얻었음. 두 번째로 풀 때는 두 번째로 큰 원소의 개수를 구하는 걸 못 했다.

 

 

 

 

문제 3. 숫자 카드 게임

 

 

문제 4. 1이 될 때까지

어떤 수 N이 1이 될 때까지 다음의 두 과정 중 하나를 반복적으로 선택하여 수행하려고 한다. 단, 두 번째 연산은 N이 K로 나누어떨어질 때만 선택할 수 있다.

 

1. N에서 1을 뺀다.

2. N을 K로 나눈다.

 

예를 들어 N = 17, K = 4일 경우

1) 17 - 1 = 16

2) 16 // 4 = 4

3) 4 // 4 = 1

 

전체 과정을 실행한 횟수는 3이 된다. 이는 N을 1로 만드는 최소 횟수이다. 

 

내 풀이

#include <iostream>
using namespace std;

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);

	int n, k, cnt=0;
	cin >> n;
	cin >> k;

	while (n != 1) {
		if ((n % k) == 0) {
			n /= k;
		}
		else {
			n -= 1;
		}
		cnt++;
	}
	cout << cnt << "\n";
}

 

답지

#include <iostream>
using namespace std;

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);

	int n, k, result=0;
	cin >> n;
	cin >> k;
	
	while (true) {
		int target = (n / k) * k;
		result += (n - target);

		n = target;

		if (n < k)
			break;
		result++;
		n /= k;
	}
	result += (n - 1);
	cout << result << "\n";
}

 

 

 

기출 문제 1. 모험가 길드

한 마을에 모험가가 N명 있습니다. 공포도가 X인 모험가는 반드시 X명 이상으로 구성한 모험가 그룹에 참여해야 여행을 떠날 수 있도록 규정했습니다.
N명의 모험가에 대한 정보가 주어졌을 때, 여행을 떠날 수 있는 그룹 수의 최댓값을 구하는 프로그램을 작성하세요.

입력 예시

5
2 3 1 2 2

출력 예시

2

 

 

내 풀이

#include <iostream>
using namespace std;

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);

	int n;
	cin >> n;
	

	int cnt = 0, max = 0, cur = 0;
	for (int i = 0; i < n; i++) {
		int num;
		cin >> num;
		if (max < num) {
			max = num;
		}
		cur++;
		if (max == cur) {
			cnt++;
			max = 0;
			cur = 0;
		}
	}
	
	cout << cnt << "\n";
}

 

답지 및 아이디어

공포도 기준으로 오름차순으로 정렬하면 항상 최소한의 모험가의 수를 포함하여 그룹을 결성할 수 있다. 앞에서부터 공포도를 확인하여 모험가의 공포도와 그룹에 결성된 모험가의 수를 비교하고 크거나 같다면 그룹으로 결성하면 된다.

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);

	int n;
	cin >> n;

	vector<int> v;
	int num;
	cin >> num;
	v.push_back(num);

	sort(v.begin(), v.end());

	int cnt = 0, cur = 0;
	for (int i = 0; i < n; i++) {
		cur++;
		if (v[i] >= cur) {
			cnt++;
			cur = 0;
		}
	}
	cout << cnt << "\n";
}

 

답지 본 후 피드백

오름차순으로 해야 최소한의 모험가의 수, 즉 최대한의 그룹 수를 구할 수 있다.

 

 

 

기출 2. 곱하기 혹은 더하기

각 자리가 숫자(0부터 9)로만 이루어진 문자열 S가 주어졌을 때, 왼쪽부터 오른쪽으로 하나씩 모든 숫자를 확인하며 숫자 사이에 'x' 혹은 '+' 연산자를 넣어 결과적으로 만들어질 수 있는 가장 큰 수를 구하는 프로그램을 작성하세요. 단, +보다 x를 먼저 계산하는 일반적인 방식과는 달리, 모든 연산은 왼쪽에서부터 순서대로 이루어진다고 가정합니다.

입력 예시1

02984

출력 예시1

576

입력 예시2

567

출력 예시2

210

 

내 풀이

#include <iostream>
using namespace std;

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);

	int arr[20] = {};
	string s;
	cin >> s;
	int length = s.length();
	for (int i = 0; i < length; i++) {
		arr[i] = s[i] - '0';
	}

	int result = arr[0];
	for (int i = 1; i < length; i++) {
		if (arr[i - 1] == 0) {
			result += arr[i];
		}
		else {
			result *= arr[i];
		}
	}
	cout << result << "\n";
}

 

답지 및 아이디어

대부분의 경우는 +보다 *가 값을 더 크게 만든다. 다만 두 수 중에서 하나라도 0이거나 1이면 더하기를 수행하는 것이 효율적이다.

#include <iostream>
using namespace std;
string str;

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);

	cin >> str;

	long long result = str[0] - '0';

	for (int i = 1; i < str.size(); i++) {
		int num = str[i] - '0';
		if (num <= 1 || result <= 1)
			result += num;
		else
			result *= num;
	}
	cout << result << "\n";

}

 

답지 본 후 피드백

1도 검사해 주어야 한다. 불필요하게 for문을 두 번 돌렸다. 그래도 로직은 어느 정도 비슷하니까 장하다.

 

 

 

 

 

 

 

 

'알고리즘 > 이것이 코딩 테스트다' 카테고리의 다른 글

08: 다이나믹 프로그래밍  (0) 2022.03.27
07: 이진 탐색  (0) 2022.03.25
06: 정렬  (0) 2022.03.20
05: DFS, BFS  (0) 2022.03.14
04: 구현  (0) 2022.03.13

+ Recent posts