그리디 알고리즘
현재 상황에서 지금 당장 좋은 것만 고르는 방법
그리디로 얻은 해가 최적의 해를 보장할 수 있는가?에 대한 정당성을 늘 고민할 것
🚀 그리디하게 해결하려고 할 때 다루기 쉬운 자료구조: 배열, 힙, 스택 등
🚀 주요 아이디어: 정렬, 조건에 맞춰 pop
🚀 알고 있는 걸 잘 꺼내자.
문제풀이
✅ 2839번 설탕배달 (S4)
1931번: 회의실 배정
(1,4), (5,7), (8,11), (12,14) 를 이용할 수 있다.
www.acmicpc.net
def getInput():
return int(input())
def solution():
n = getInput()
total = n
bucket = 0
# case 1
if n % 5 == 0:
return n // 5
while total > 0:
if total == 1 or total == 2:
return -1
if total % 5 == 0:
return bucket + (total // 5)
total -= 3
bucket += 1
return bucket
print(solution())
✅ 1931번 회의실 배정 (S1)
1931번: 회의실 배정
(1,4), (5,7), (8,11), (12,14) 를 이용할 수 있다.
www.acmicpc.net
# 시간 복잡도
def getInput():
N = int(input())
meeting = []
for i in range(N):
meeting.append(list(map(int, input().split())))
return meeting
def solution():
schedules = getInput()
# 정렬 우선 순위 (endTime > startTime)
target_arr = sorted(schedules, key=lambda x: (x[1], x[0]))
curr = 0
result = 0
for start, end in target_arr:
if curr <= start:
result += 1
curr = end
return result
print(solution())
✅ 1202번 보석 도둑 (G2)
- key:
- 가방에는 최대 한 개의 보석만 넣을 수 있다.
- 훔칠 수 있는 보석의 최대 가격
1202번: 보석 도둑
첫째 줄에 N과 K가 주어진다. (1 ≤ N, K ≤ 300,000) 다음 N개 줄에는 각 보석의 정보 Mi와 Vi가 주어진다. (0 ≤ Mi, Vi ≤ 1,000,000) 다음 K개 줄에는 가방에 담을 수 있는 최대 무게 Ci가 주어진다. (1 ≤ Ci
www.acmicpc.net
import heapq
def getInput():
N, K = map(int, input().split())
jewerly = []
for _ in range(N):
jewerly.append(list(map(int, input().split())))
bags = []
for _ in range(K):
bags.append(int(input()))
jewerly.sort(reverse=True)
bags.sort()
return jewerly, bags
def solution():
jewerly, bags = getInput()
result = 0
heap = [] # 가격이 큰 보석이 우선순위가 되니까 price에 대한 maxHeap
# 작은 가방 기준으로 보석 보기
for bag in bags:
while jewerly:
w, p = jewerly[-1][0], jewerly[-1][1]
if w > bag:
break
heapq.heappush(heap, -p) # maxHeap
jewerly.pop()
# 담을 수 있는 쥬얼리 후보, 어차피 여기 들어가면 다음 가방에도 들어가기 때문에 남아 있어도 문제 X
if heap: # 그 중 가장 가치 있는 쥬얼리 담고 넘어감
result += (-1) * heapq.heappop(heap)
return result
print(solution())
✅ 1715번 카드 정렬하기 (G4)
1715번: 카드 정렬하기
정렬된 두 묶음의 숫자 카드가 있다고 하자. 각 묶음의 카드의 수를 A, B라 하면 보통 두 묶음을 합쳐서 하나로 만드는 데에는 A+B 번의 비교를 해야 한다. 이를테면, 20장의 숫자 카드 묶음과 30장
www.acmicpc.net
import heapq
def getInput():
N = int(input())
arr = [int(input()) for _ in range(N)]
return arr
def solution():
cards = getInput()
if len(cards) == 1:
return 0
heapq.heapify(cards) # 최소 힙으로 카드 크기를 관리, 가장 작은 크기의 카드 두 장 선택 하기 위함
result = 0
while len(cards) > 1:
a = heapq.heappop(cards)
b = heapq.heappop(cards)
cost = a + b
result += cost
heapq.heappush(cards, cost) # 합친 결과를 다시 카드 묶음에 추가
return result
print(solution())
✅ 11000번 강의실 배정 (G5)
startTime 기준으로 정렬하고, endTime 간의 비교를 통해 병렬로 필요한 강의실 개수를 구할 수 있다.
11000번: 강의실 배정
첫 번째 줄에 N이 주어진다. (1 ≤ N ≤ 200,000) 이후 N개의 줄에 Si, Ti가 주어진다. (0 ≤ Si < Ti ≤ 109)
www.acmicpc.net
import heapq
def getInput():
N = int(input())
lecture = []
for i in range(N):
lecture.append(list(map(int, input().split())))
sorted_lecture = sorted(lecture, key=lambda x: x[0])
return sorted_lecture, N
def solution():
lecture, n = getInput()
heap = []
initial_end_time = lecture[0][1]
heapq.heappush(heap, initial_end_time)
for i in range(1, n):
curr_start = lecture[i][0]
curr_end = lecture[i][1]
standard = heap[0]
if curr_start < standard:
heapq.heappush(heap, curr_end)
continue
heapq.heappop(heap)
heapq.heappush(heap, curr_end)
return len(heap)
print(solution())
✅ 1744번 수 묶기 (G4)
적절한 케이스 분류에 대한 아이디어 (예: 음수, 양수)
1744번: 수 묶기
길이가 N인 수열이 주어졌을 때, 그 수열의 합을 구하려고 한다. 하지만, 그냥 그 수열의 합을 모두 더해서 구하는 것이 아니라, 수열의 두 수를 묶으려고 한다. 어떤 수를 묶으려고 할 때, 위치에
www.acmicpc.net
def getInput():
N = int(input())
negative_nums = [] # 음수를 저장할 배열
positive_nums = [] # 양수를 저장할 배열
for _ in range(N):
num = int(input())
if num <= 0:
negative_nums.append(num)
else:
positive_nums.append(num)
# 음수 배열은 절댓값이 큰 순서대로 정렬
negative_nums.sort()
# 양수 배열은 큰 수부터 정렬
positive_nums.sort(reverse=True)
return positive_nums, negative_nums
def solution():
pos, neg = getInput()
result = 0
# 음수 배열 처리
for i in range(0, len(neg), 2):
if i == len(neg) - 1:
result += neg[i]
continue
result += neg[i] * neg[i+1]
# 양수 배열 처리
for i in range(0, len(pos), 2):
if i == len(pos) - 1:
result += pos[i]
continue
result += max(pos[i] * pos[i+1], pos[i] + pos[i+1])
return result
print(solution())
✅ 1339번 단어 수학 (G4)
1339번: 단어 수학
첫째 줄에 단어의 개수 N(1 ≤ N ≤ 10)이 주어진다. 둘째 줄부터 N개의 줄에 단어가 한 줄에 하나씩 주어진다. 단어는 알파벳 대문자로만 이루어져있다. 모든 단어에 포함되어 있는 알파벳은 최대
www.acmicpc.net
가중치 계산 예시
단어: APPLE, EAT
A: 10000 (from APPLE) + 10 (from EAT) = 10010
P: 1100 (from APPLE) = 1100
L: 10 (from APPLE) = 10
E: 1 (from APPLE) + 100 = 101
T: 1 (from EAT) = 1
순서대로 A에 9, P에 8, E에 7, L에 6, T에 5 부여하여
90090 + 8800 + 707 + 60 + 5
= 99662
def getInput():
n = int(input())
words = [input() for _ in range(n)]
return words
def solution():
words = getInput()
result = 0
# 모든 알파벳의 가중치를 계산, 저장할 배열
alphabet = [0] * 26
# 각 단어의 가중치 값을 계산
for word in words:
for i, char in enumerate(word):
alphabet[ord(char) - ord('A')] += 10 ** (len(word) - i - 1)
# 알파벳의 가중치 오름차순으로 정렬
alphabet.sort()
# 가중치가 큰 알파벳부터, 9부터 1까지 할당 (0이면 더해도 0이니 무시)
for i in range(9, 0, -1):
result += alphabet.pop() * i
return result
print(solution())
❌✅ 1700번 멀티탭 스케줄링 (G1)
가장 나중에 사용될 기기의 인덱스를 교체
1700번: 멀티탭 스케줄링
기숙사에서 살고 있는 준규는 한 개의 멀티탭을 이용하고 있다. 준규는 키보드, 헤어드라이기, 핸드폰 충전기, 디지털 카메라 충전기 등 여러 개의 전기용품을 사용하면서 어쩔 수 없이 각종 전
www.acmicpc.net
def getInput():
N, K = map(int, input().split()) # 기기 개수와 멀티탭 구멍 개수 입력
plugs = list(map(int, input().split())) # 각 기기의 사용 순서 입력
return N, K, plugs
def solution():
n, k, plugs = getInput() # 입력 받기
result = 0 # 멀티탭 뽑는 횟수 초기화
multi_tap = [] # 현재 멀티탭에 꽂힌 기기들을 저장할 리스트
for i in range(k): # 각 사용 순서마다 확인
if plugs[i] in multi_tap: # 이미 해당 기기가 멀티탭에 꽂혀 있는 경우 스킵
continue
if len(multi_tap) < n: # 멀티탭에 빈 자리가 있는 경우
multi_tap.append(plugs[i]) # 기기를 멀티탭에 꽂음
continue
# 그리디하게 제거하기
remove_idx = -1
latest_idx = -1 # 현재 멀티탭에 꽂혀있는 기기 중에서 가장 나중에 사용될 기기의 인덱스
for curr_idx in range(n): # 멀티탭에 꽂혀 있는 기기들 중에서 어떤 기기를 뽑을지 결정
remains = plugs[i + 1:] # 현재 기기 이후에 사용할 기기들
curr_device = multi_tap[curr_idx]
if curr_device not in remains: # 나중에 사용하지 않을 경우
remove_idx = curr_idx
break
if remains.index(curr_device) > latest_idx: # 더 나중에 사용되는 경우
latest_idx = remains.index(curr_device)
remove_idx = curr_idx
multi_tap[remove_idx] = plugs[i] # 선택된 기기로 멀티탭 교체
result += 1
return result
print(solution())
✅ 2812번 크게 만들기 (G3)
n 길이 배열에서 앞에서부터 작은 k개의 원소를 제거하는 방법 (O(n) 풀 수 있음)
2812번: 크게 만들기
N자리 숫자가 주어졌을 때, 여기서 숫자 K개를 지워서 얻을 수 있는 가장 큰 수를 구하는 프로그램을 작성하시오.
www.acmicpc.net
def getInput():
n, k = map(int, input().split())
num = list(input())
return k, num
# O(n^2) 불가
# 앞에서부터 작은 수 k개 지우기
def solution():
remain, num = getInput()
stack = []
for i in range(len(num)):
# 스택에서 원소를 뺄 수 있고, 스택 top에 있는 게 현재 숫자보다 작으면
while stack and remain > 0 and stack[-1] < num[i]:
stack.pop()
remain -= 1
stack.append(num[i])
return ''.join(stack[:len(stack) - remain])
print(solution())
✅ 3109번 빵집 (G2)
3109번: 빵집
유명한 제빵사 김원웅은 빵집을 운영하고 있다. 원웅이의 빵집은 글로벌 재정 위기를 피해가지 못했고, 결국 심각한 재정 위기에 빠졌다. 원웅이는 지출을 줄이고자 여기저기 지출을 살펴보던
www.acmicpc.net
def getInput():
R, C = map(int, input().split())
board = [input() for _ in range(R)]
return R, C, board
def is_valid(x, y, R, C):
return 0 <= x and x < R and 0 <= y and y < C
def solution():
R, C, board = getInput()
result = 0
BLANK = '.'
BUILDING = 'x'
def dfs(x, y): # 파이프를 연결해 가스관까지 도달하는 경로 찾기
if y == C - 1:
return True
dx = [-1, 0, 1]
for d in dx:
nx, ny = x + d, y + 1
if is_valid(nx, ny, R, C) and board[nx][ny] == BLANK:
board[nx] = board[nx][:ny] + BUILDING + board[nx][ny + 1:]
if dfs(nx, ny): # 경로 찾음
return True
return False
for i in range(R): # 각 행에 대한 dfs 수행
if dfs(i, 0):
result += 1
return result
print(solution())
✅ 2437번 저울 (G2)
2437번: 저울
하나의 양팔 저울을 이용하여 물건의 무게를 측정하려고 한다. 이 저울의 양 팔의 끝에는 물건이나 추를 올려놓는 접시가 달려 있고, 양팔의 길이는 같다. 또한, 저울의 한쪽에는 저울추들만 놓
www.acmicpc.net
# 추들을 사용해 측정할 수 없는 양의 정수 무게 중 최솟값을 구하는 프로그램
def getInput():
n = int(input())
weights = list(map(int, input().split()))
return weights
def solution():
weights = getInput()
weights.sort() # 무게추를 정렬
total_weight = 0 # 측정 가능한 무게 범위의 최대값
for weight in weights:
# 현재 무게를 측정할 수 없는 경우
if total_weight + 1 < weight:
break
total_weight += weight
return total_weight + 1 # 가능한 한계 + 1 해야 측정할 수 없는 값임
print(solution())
❌✅ 10775번 공항 (G2)
10775번: 공항
예제 1 : [2][?][?][1] 형태로 도킹시킬 수 있다. 3번째 비행기는 도킹시킬 수 없다. 예제 2 : [1][2][3][?] 형태로 도킹 시킬 수 있고, 4번째 비행기는 절대 도킹 시킬 수 없어서 이후 추가적인 도킹은 불
www.acmicpc.net
# 입력을 받는 함수
def getInput():
G = int(input())
P = int(input())
planes = [int(input()) for _ in range(P)]
return G, P, planes
def find(gate, x):
if gate[x] != x:
gate[x] = find(gate, gate[x])
return gate[x]
def union(gate, a, b):
a = find(gate, a)
b = find(gate, b)
if a < b:
gate[b] = a
else:
gate[a] = b
def solution():
G, P, planes = getInput()
gates = [i for i in range(G + 1)] # 각 게이트의 부모를 자기 자신으로 초기화
result = 0 # 도킹된 비행기 개수 초기화
for plane in planes: # 각 비행기에 대해서 처리
docked_gate = find(gates, plane) # 비행기가 도킹할 게이트의 루트 찾기
if docked_gate == 0: # 더 이상 도킹할 수 없는 경우 종료
break
union(gates, docked_gate, docked_gate - 1) # 현재 게이트와 직전 게이트를 합치기
result += 1 # 비행기가 도킹되었으므로 결과 개수 증가
return result
print(solution())