💻코딩테스트

[이코테_코딩테스트] 그리디 유형 문제 풀이

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로 나누어 떨어질 때만 선택할 수 있다.
      1. N에서 1을 뺍니다.
      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

[문제] 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

[문제] 곱하기 혹은 더하기 - 문제 해결 아이디어

  • 대부분의 경우 ‘+’보다는 ‘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
 

[문제] 모험가 길드 - 문제 해결 아이디어

  • 오름차순 정렬 이후 공포도가 가장 낮은 모험가부터 확인
  • 앞에서부터 공포도를 확인하며 ‘현재 그룹에 포함된 모험가의 수’ ≥ ‘현재 확인하고 있는 공포도’ 라면 그룹으로 설정!
  • 공포도가 오름차순으로 정렬되어 있으니, 항상 최소한의 모험가의 수만 포함하여 그룹을 결성한다.

[문제] 모험가 길드 - 나의 답안 (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. 그리디 & 구현” 영상을 보고 작성하였습니다.