💻코딩테스트
[이코테_코딩테스트] 그리디 유형 문제 풀이
date
Jul 13, 2023
slug
coding-test-greedy-example
author
status
Public
tags
Python
코딩테스트
summary
그리디 유형 문제를 풀어보자
type
Post
thumbnail
category
💻코딩테스트
updatedAt
Jul 13, 2023 02:09 PM
[문제] 1이 될 때까지
- 어떠한 수 N이 1이 될 때까지 다음의 두 과정 중 하나를 반복적으로 선택하여 수행하려고 한다.
- 단, 두 번째 연산은 N이 K로 나누어 떨어질 때만 선택할 수 있다.
- N에서 1을 뺍니다.
- N을 K로 나눕니다.
- 예를 들어 N이 17, K가 4라고 가정합시다. 이때 1번의 과정을 한 번 수행하면 N은 16이 됩니다. 이후에 2번의 과정을 두 번 수행하면 N은 1이 됩니다. 결과적으로 이경우 전체 과정을 실행한 횟수는 3이 됩니다. 이는 N을 1로 만드는 최소 횟수입니다.
- N과 K가 주어질 때 N이 1이 될 때까지 1번 혹은 2번의 과정을 수행해야 하는 최소 횟수를 구하는 프로그램을 작성하세요.
[문제] 1이 될 때까지 - 문제 조건
![notion image](https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F5923a54f-1d05-4741-be48-95c1d0099561%2FUntitled.png?table=block&id=8ddf7d36-39b7-4647-bf58-f44d57107381&cache=v2)
[문제] 1이 될 때까지 - 문제 해결 아이디어
- 주어진 N에 대하여 최대한 많이 나누기를 수행하면 됨!
- N의 값을 줄일 때 2 이상의 수로 나누는 작업이 1을 빼는 작업보다 수를 많이 줄일 수 있다.
N = 25, K = 3일 때의 예시
Step | 연산 과정 | N 값 |
Step 0 | ㅤ | 25 |
Step 1 | N에서 1 빼기 | 24 |
Step 2 | N을 K로 나누기 | 8 |
Step 3 | N에서 1 빼기 | 7 |
Step 4 | N에서 1 빼기 | 6 |
Step 5 | N을 K로 나누기 | 2 |
Step 6 | N에서 1 빼기 | 1 |
[문제] 1이 될 때까지 - 정당성 분석
- 가능하면 최대한 많이 나누는 작업이 최적의 해를 항상 보장하는가?
- N이 아무리 큰 수여도, K로 계속 나눈다면 기하급수적으로 빠르게 줄일 수 있다.
- K가 2 이상이기만 하면 K로 나누는 것이 1을 빼는 것보다 항상 빠르게 N을 줄일 수 있다.
- 또한 N은 항상 1에 도달한다.
[문제] 1이 될 때까지 - 나의 답안 (Python)
n, k = map(int, input().split()) count = 0 while(n != 1): if n % k == 0: n //= k count += 1 else: n -= 1 count += 1 print(count)
[문제] 1이 될 때까지 - 답안 예시 (Python)
# N, K공백을 기준으로 구분하여 입력 받기 n, k = map(int, input().split()) result = 0 while True: # N이 K로 나누어 떨어지는 수가 될 때까지만 1씩 빼기 target = (n // k) * k result += (n - target) n = target # N이 K보다 작을 때 (더 이상 나눌 수 없을 때) 반복문 탈출 if n < k: break # K로 나누기 result += 1 n //= k # 마지막으로 남은 수에 대하여 1씩 빼기 result += (n - 1) print(result)
- target을 활용해 몇 번 1을 빼야 나누어 떨어지는지 알 수 있다.
- 예시 답안에서 target을 활용해 시간 복잡도를 줄이는 테크닉을 기억해야겠다!
[문제] 1이 될 때까지 - 답안 예시 (Java)
public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); // N, K를 공백을 기준으로 구분하여 입력 받기 int n = sc.nextInt(); int k = sc.nextInt(); int result = 0; while (true) { // N이 K로 나누어 떨어지는 수가 될 때까지만 1씩 빼기 int target = (n / k) * k; result += (n - target); n = target; // N이 K보다 작을 때 (더 이상 나눌 수 없을 때) 반복문 탈출 if (n < k) break; // K로 나누기 result += 1; n /= k; } // 마지막으로 남은 수에 대하여 1씩 빼기 result += (n - 1); System.out.println(result); } }
[문제] 곱하기 혹은 더하기
- 각 자리가 숫자(0부터 9)로만 이루어진 문자열 S가 주어졌을 때, 왼쪽부터 오른쪽으로 하나씩 모든 숫자를 확인하며 숫자 사이에 ‘x’ 혹은 ‘+’ 연산자를 넣어 결과적으로 만들어질 수 있는 가장 큰 수를 구하는 프로그램을 작성하세요.
- 단, ‘+’보다 ‘x’를 먼저 계산하는 일반적인 방식과는 달리, 모든 연산은 왼쪽에서부터 순서대로 이루어진다고 가정합니다.
- 예를 들어 02984라는 문자열로 만들 수 있는 가장 큰 수는 ((((0+2) x 9) x 8 ) x 4) = 576입니다. 또한 만들어질 수 있는 가장 큰 수는 항상 20억 이하의 정수가 되도록 입력이 주어집니다.
[문제] 곱하기 혹은 더하기 - 문제 조건
![notion image](https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fee990419-5f49-419f-a4d9-98137ddc10d7%2FUntitled.png?table=block&id=3ff9594f-2ccc-4fc0-a8e3-77c90b4f35e0&cache=v2)
[문제] 곱하기 혹은 더하기 - 문제 해결 아이디어
- 대부분의 경우 ‘+’보다는 ‘x’가 더 값을 크게 만든다.
- 다만 두 수 중에서 하나라도 ‘0’ 혹은 ‘1’인 경우, 곱하기보다는 더하기를 수행하는 것이 효율적
- 따라서 두 수에 대하여 연산을 수행할 때, 두 수 중에서 하나라도 1이하인 경우에는 더함, 두 수가 모두 2이상인 경우에는 곱하면 된다.
[문제] 곱하기 혹은 더하기 - 나의 답안 (Python)
str_num = str(input()) result = 1 for i in range(len(str_num)): if str_num[i] == '0' or str_num[i] == '1': temp = int(str_num[i]) continue result *= int(str_num[i]) + temp temp = 0 print(result)
[문제] 곱하기 혹은 더하기 - 답안 예시 (Python)
data = input() # 첫 번째 문자를 숫자로 변경하여 대입 result = int(data[0]) for i in range(1, len(data)): # 두 수 중에서 하나라도 '0' 혹은 '1'인 경우, 곱하기보다는 더하기 수행 num = int(data[i]) if num <= 1 or result <= 1: result += num else: result *= num print(result)
[문제] 곱하기 혹은 더하기 - 답안 예시 (Java)
import java.util.*; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); String str = sc.next(); // 첫 번째 문자를 숫자로 변경한 값을 대입 long result = str.charAt(0) - '0'; for (int i = 1; i < str.length(); i++) { // 두 수 중에서 하나라도 '0' 혹은 '1'인 경우, 곱하기보다는 더하기 수행 int num = str.charAt(i) - '0'; if (num <= 1 || result <= 1) { result += num; } else { result *= num; } } System.out.println(result); } }
[문제] 모험가 길드
- 한 마을에 모험가가 N명 있다. 모험가 길드에서는 N명의 모험가를 대상으로 ‘공포도’를 측정했는데, ‘공포도’가 높은 모험가는 쉽게 공포를 느껴 위험 상황에서 제대로 대처할 능력이 떨어집니다.
- 모험가 길드장인 동빈이는 모험가 그룹을 안전하게 구성하고자 공포도가 X인 모험가는 반드시 X명 이상으로 구성한 모험가 그룹에 참여해야 여행을 떠날 수 있도록 규정했습니다.
- 동빈이는 최대 몇 개의 모험가 그룹을 만들 수 있는지 궁금합니다. N명의 모험가에 대한 정보가 주어졌을 때, 여행을 떠날 수 있는 그룹 수의 최댓값을 구하는 프로그램을 작성하세요.
- 예를 들어 N = 5이고, 각 모험가의 공포도가 “2 3 1 2 2”이라고 가정해봅시다.
- 이 경우 그룹 1에 공포도가 1, 2, 3인 모험가를 한 명씩 넣고, 그룹 2에 공포도가 2인 남은 두 명을 넣게되면 총 2개의 그룹을 만들 수 있다.
- 또한 몇 명의 모험가는 마을데 그대로 남아 있어도 되기 때문에, 모든 모험가를 특정한 그룹에 넣을 필요는 없다.
[문제] 모험가 길드 - 문제 조건
![notion image](https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F9b752a13-0ecb-4d86-b130-68f01c92e414%2FUntitled.png?table=block&id=53d94f2c-fee9-49f9-a490-02403099b083&cache=v2)
[문제] 모험가 길드 - 문제 해결 아이디어
- 오름차순 정렬 이후 공포도가 가장 낮은 모험가부터 확인
- 앞에서부터 공포도를 확인하며 ‘현재 그룹에 포함된 모험가의 수’ ≥ ‘현재 확인하고 있는 공포도’ 라면 그룹으로 설정!
- 공포도가 오름차순으로 정렬되어 있으니, 항상 최소한의 모험가의 수만 포함하여 그룹을 결성한다.
[문제] 모험가 길드 - 나의 답안 (Python)
n = int(input()) fear_array = sorted(list(map(int, input().split()))) result = 0 for i in fear_array: if n - i >= 0: result += 1 n -= 1 print(result)
[문제] 모험가 길드 - 답안 예시 (Python)
n = int(input()) data = list(map(int, input().split())) data.sort() result = 0 # 총 그룹의 수 count = 0 # 현재 그룹에 포함된 모험가의 수 for i in data: # 공포도를 낮은 것부터 하나씩 확인하며 count += 1 # 현재 그룹에 해당 모험가를 포함시키기 if count >= i: # 현재 그룹에 포함된 모험가의 수가 현재의 공포도 이상이라면, 그룹 결성 result += 1 # 총 그룹의 수 증가시키기 count = 0 # 현재 그룹에 포함된 모험가의 수 초기화 print(result) # 총 그룹의 수 출력
[문제] 모험가 길드 - 답안 예시 (Java)
import java.util.*; public class Main { public static int n; public static ArrayList<Integer> arrayList = new ArrayList<>(); public static void main(String[] args) { Scanner sc = new Scanner(System.in); n = sc.nextInt(); for (int i = 0; i < n; i++) { arrayList.add(sc.nextInt()); } Collections.sort(arrayList); int result = 0; // 총 그룹의 수 int count = 0; // 현재 그룹에 포함된 모험가의 수 for (int i = 0; i < n; i++) { // 공포도를 낮은 것부터 하나씩 확인하며 count += 1; // 현재 그룹에 해당 모험가를 포함시키기 if (count >= arrayList.get(i)) { // 현재 그룹에 포함된 모험가의 수가 현재의 공포도 이상이라면, 그룹 결성 result += 1; // 총 그룹의 수 증가시키기 count = 0; // 현재 그룹에 포함된 모험가의 수 초기화 } } System.out.println(result); } }
이 글은 유튜브 “동빈나” 채널의 “(이코테 2021 강의 몰아보기) 2. 그리디 & 구현” 영상을 보고 작성하였습니다.