728x90
반응형
Greedy (탐욕법)
: 현재 State에서 가장 좋은 것을 선택
‘가장 큰 순서대로’, ‘가 장 작은 순서대로’와 같은 기준을 알게 모르게 제시, 주로 정렬 알고리즘과 함께 출제
Sample Question 1) 거스름돈 문제
당신은 음식점의 계산을 도와주는 점원이다. 카운터에는 거스름돈으로 사용할 500원, 100원, 50 원, 10원짜리 동전이 무한히 존재한다고 가정한다. 손님에게 거슬러 줘야 할 돈이 N원일 때 거슬러 줘야 할 동전의 최소 개수를 구하라. 단, 거슬러 줘야 할 돈 N은 항상 10의 배수이다.
비싼 화폐 단위부터 거슬러주기 : 500, 100, 50, 10
n = 1260
count = 0
# 큰 단위의 화폐부터 차례대로 확인
coin_types = [500, 100, 50, 10]
for coin in coin_types:
count += n // coin # 해당 화폐로 거슬러 줄 수 있는 동전의 개수 세기
n = n % coin
print(count)
Sample Question 2) 큰 수의 법칙
‘큰 수의 법칙’은 일반적으로 통계 분야에서 다루어지는 내용이지만 동빈이는 본인만의 방식으로 다르게 사용하고 있다. 동빈이의 큰 수의 법칙은 다양한 수로 이루어진 배열이 있을 때 주어진 수들을 M번 더하여 가장 큰 수를 만드는 법칙이다. 단, 배열의 특정한 인덱스(번호)에 해당하는 수가 연속 해서 K번을 초과하여 더해질 수 없는 것이 이 법칙의 특징이다.
예를 들어 순서대로 2, 4, 5, 4, 6으로 이루어진 배열이 있을 때 M이 8이고, K가 3이라고 가정하자.
이 경우 특정한 인덱스의 수가 연속해서 세 번까지만 더해질 수 있으므로 큰 수의 법칙에 따른 결과는 6 + 6 + 6 + 5 + 6 + 6 + 6 + 5인 46이 된다. 단, 서로 다른 인덱스에 해당하는 수가 같은 경우에도 서로 다른 것으로 간주한다.
예를 들어 순서대로 3, 4, 3, 4, 3으로 이루어진 배열이 있을 때 M이 7이고, K가 2라고 가정하자.
이 경우 두 번째 원소에 해당하는 4와 네 번째 원소에 해당하는 4를 번갈아 두 번씩 더하는 것이 가능하다.
결과적으로 4 + 4 + 4 + 4 + 4 + 4 + 4인 28이 도출된다.
배열의 크기 N, 숫자가 더해지는 횟수 M, 그리고 K가 주어질 때 동빈이의 큰 수의 법칙에 따른 결과를 출력하시오.
입력 조건 :
• 첫째 줄에 N(2 ≤ N ≤ 1,000), M(1 ≤ M ≤ 10,000), K(1 ≤ K ≤ 10,000)의 자연수가 주어지며, 각 자연수는 공백으로 구분한다.
• 둘째 줄에 N개의 자연수가 주어진다. 각 자연수는 공백으로 구분한다. 단, 각각의 자연수는 1 이상 10,000 이하의 수로 주어진다.
• 입력으로 주어지는 K는 항상 M보다 작거나 같다
출력 조건 :
•첫째 줄에 동빈이의 큰 수의 법칙에 따라 더해진 답을 출력한다.
입력 예시
5 8 3
2 4 5 4 6
출력 예시
46
여기서의 관건은 가장 큰 수와 두번째로 큰 수만 저장해서 연속으로 더하기만 하면 된다.
아래는 내가 작성했던 코드인데, 실제 예시 답안과는 약간 차이가 존재하다.
이렇게 작성할 경우 내 코드의 실행 시간이 어떻게 될지 모르겠어서 효율적인 것이라 보기 힘들 수 도 있을것 같다.
n, m, k = map(int, input().split())# N, M, k를 공백으로 구분해 입력받기
temp_numbers = list(map(int, input().split()))# 연산에 사용할 리스트 수 입력받기
numbers = temp_numbers[:n]# N개의 수만을 계산하기 위해 따로 변수 선언
sorted_numbers = sorted(numbers, reverse=True)# 역순으로 정렬
sum_numbers = []# 실제 계산 과정에 사용될 list
i = 0 # 더한 횟수
while i < m: # 최대 M까지만 동작
if i % k == 0 and i != 0: # i가 k의 배수가 될 때마다 두번째 큰 숫자를 더하는 방식
sum_numbers.append(sorted_numbers[1])
i+=1
continue
sum_numbers.append(sorted_numbers[0]) # 그게 아니면, 일반적으로 가장 큰 숫자를 더하기
i+=1
result = sum(sum_numbers) # 최종 답안 출력
print(result)
#############아래 : 예시 코드###############
# N, M, K를 공백으로 구분하여 입력받기
n, m, k = map(int, input().split())
# N개의 수를 공백으로 구분하여 입력받기
data = list(map(int, input().split()))
data.sort() # 입력받은 수들 정렬하기
first = data[n - 1] # 가장 큰 수
second = data[n - 2] # 두 번째로 큰 수
result = 0
while True:
for i in range(k): # 가장 큰 수를 K번 더하기
if m = = 0: # m이 0이라면 반복문 탈출
break
result += first
m -= 1 # 더할 때마다 1씩 빼기
if m = = 0: # m이 0이라면 반복문 탈출
break
result += second # 두 번째로 큰 수를 한 번 더하기
m -= 1 # 더할 때마다 1씩 빼기
print(result) # 최종 답안 출력
Sample Question 3) 숫자 카드 게임
숫자 카드 게임은 여러 개의 숫자 카드 중에서 가장 높은 숫자가 쓰인 카드 한 장을 뽑는 게임이다.
단, 게임의 룰을 지키며 카드를 뽑아야 하고 룰은 다음과 같다.
1. 숫자가 쓰인 카드들이 N × M 형태로 놓여 있다. 이때 N은 행의 개수를 의미하며, M은 열 의 개수를 의미한다.
2. 먼저 뽑고자 하는 카드가 포함되어 있는 행을 선택한다.
3. 그다음 선택된 행에 포함된 카드들 중 가장 숫자가 낮은 카드를 뽑아야 한다.
4. 따라서 처음에 카드를 골라낼 행을 선택할 때, 이후에 해당 행에서 가장 숫자가 낮은 카드를 뽑을 것을 고려하여 최종적으로 가장 높은 숫자의 카드를 뽑을 수 있도록 전략을 세워야 한다.
예를 들어 3 × 3 형태로 카드들이 다음과 같이 놓여 있다고 가정하자.
여기서 카드를 골라낼 행을 고를 때 첫 번째 혹은 두 번째 행을 선택하는 경우, 최종적으로 뽑는 카드는 1이다.
하지만 세 번째 행을 선택하는 경우 최종적으로 뽑는 카드는 2이다.
따라서 이 예제에 서는 세 번째 행을 선택하여 숫자 2가 쓰여진 카드를 뽑는 것이 정답이다.
카드들이 N × M 형태로 놓여 있을 때, 게임의 룰에 맞게 카드를 뽑는 프로그램을 만드시오.
입력 조건:
•첫째 줄에 숫자 카드들이 놓인 행의 개수 N과 열의 개수 M이 공백을 기준으로 하여 각각 자연수로 주 어진다. (1 ≤ N, M ≤ 100)
•둘째 줄부터 N개의 줄에 걸쳐 각 카드에 적힌 숫자가 주어진다. 각 숫자는 1 이상 10,000 이하의 자 연수이다.
출력 조건:
•첫째 줄에 게임의 룰에 맞게 선택한 카드에 적힌 숫자를 출력한다
입력 예시 1
3 3
3 1 2
4 1 4
2 2 2
출력 예시 1
2
입력 예시 2
2 4
7 3 1 8
3 3 3 4
출력 예시 2
3
이 문제의 관건은 각 행마다 가장 작은 수를 저장해놓고 그 중 가장 큰 수를 내놓으면 되는 형태이다.
아래는 내가 작성한 코드고 그 밑에 min함수와 2중 반복문 두가지를 같이 사용하는 답안 예시가 존재한다.
내가 작성한 코드와의 큰 차이점은 NxM에 초점을 맞추다보니 M열을 초과한 데이터를 넣더라도 에러가 발생하지 않도록 중간에 M개까지만 사용하도록 작성한 부분이 있는데 예시 코드에서는 그 부분은 따로 고려하지 않은 것 같다.
n, m = map(int, input().split())# N, M 을 공백으로 구분해 입력받기
min_list=[]
for i in range(0,n):# N개의 행을 입력받기
matrix = list(map(int, input().split()))#N행을 구성하는 숫자 입력받기
matrix = matrix[:m]#M개까지의 숫자만을 사용하기 위해 matrix에 M까지만 덮어씌우기
min_list.append(min(matrix))
result = max(min_list)
print(result)
#############아래 : 예시 코드###############
# N, M을 공백으로 구분하여 입력받기
n, m = map(int, input().split())
result = 0
# 한 줄씩 입력받아 확인
for i in range(n):
data = list(map(int, input().split()))
# 현재 줄에서 '가장 작은 수' 찾기
min_value = min(data)
# '가장 작은 수'들 중에서 가장 큰 수 찾기
result = max(result, min_value)
print(result) # 최종 답안 출력
# N, M을 공백으로 구분하여 입력받기
n, m = map(int, input().split())
result = 0
# 한 줄씩 입력받아 확인
for i in range(n):
data = list(map(int, input().split()))
# 현재 줄에서 '가장 작은 수' 찾기
min_value = 10001
for a in data:
min_value = min(min_value, a)
# '가장 작은 수'들 중에서 가장 큰 수 찾기
result = max(result, min_value)
print(result) # 최종 답안 출력
Sample Question 4) 1이 될 때까지
어떠한 수 N이 1이 될 때까지 다음의 두 과정 중 하나를 반복적으로 선택하여 수행하려고 한다. 단, 두 번째 연산은 N이 K로 나누어떨어질 때만 선택할 수 있다.
1. N에서 1을 뺀다.
2. N을 K로 나눈다.
예를 들어 N이 17, K가 4라고 가정하자. 이때 1번의 과정을 한 번 수행하면 N은 16이 된다. 이후 에 2번의 과정을 두 번 수행하면 N은 1이 된다. 결과적으로 이 경우 전체 과정을 실행한 횟수는 3이 된다. 이는 N을 1로 만드는 최소 횟수이다. N과 K가 주어질 때 N이 1이 될 때까지 1번 혹은 2번의 과정을 수행해야 하는 최소 횟수를 구하는 프로그램을 작성하시오.
입력 조건 :
• 첫째 줄에 N(2 ≤ N ≤ 100,000)과 K(2 ≤ K ≤ 100,000)가 공백으로 구분되며 각각 자연수로 주어진다. 이때 입력으로 주어지는 N은 항상 K보다 크거나 같다
출력 조건 :
• 첫째 줄에 N이 1이 될 때까지 1번 혹은 2번의 과정을 수행해야 하는 횟수의 최솟값을 출력한다.
입력 예시 : 25 5
출력 예시 : 2
여기서의 관건은 일단 K로 최대한 많이 Num을 나눠 나가면서 연산 횟수를 최대한 줄이는 방향으로 진행해야 한다.
<내가 작성한 코드>
num = 27
k = 15
count = 0
while num >=k:
while num%k!=0:
num-=1
count +=1
num //= k
count +=1
while num != 1 and num < k:
if num == 1:
break
num -=1
count +=1
print(count)
#############아래 : 예시 코드###############
num = 25
k = 5
count = 0
while num >=k: # 일단 크면 계속 나누기
while num % k != 0: #Num이 K로 나누어 떨어지지 않으면 -1
num -= 1
count += 1
num = num // k # 나누어 떨어지게 되는 순간부터 나누기 진행
count +=1
while num > 1: #더이상 K로 나눌 수 없을 때.
num -= 1
count +=1
print(count)
다만, 이렇게 진행할 경우 Num의 값이 커질때 시간복잡도가 높아질 수 있으므로, 아예 Num을 K의 배수값으로 변경하는 방식도 아래와 같이 진행할 수 있다. 이렇게 진행하면, 훨씬 시간적으로 빠르게 O(NlogN)까지도 줄일 수 있다고 한다.
#############아래 : 예시 코드2###############
# 제안 최적 모델
# 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)
728x90
반응형
'Coding TEST' 카테고리의 다른 글
[코딩 테스트 with 파이썬] Chapter 05. DFS/BFS (0) | 2021.07.27 |
---|---|
[코딩 테스트 with 파이썬] Chapter 04. Implementation (2) | 2021.07.26 |
[Python] itertools 모듈 / 내장 함수(zip, map ..) (0) | 2021.06.01 |
[Codility_16. Greedy Algorithm] MaxNonoverlappingSegments (3) | 2021.05.21 |
[코딩 테스트 with 파이썬] 0. Introduction (0) | 2021.05.20 |