이번 주 학습 키워드는 동적 계획법(DP) 과 탐욕 알고리즘(Greedy), 두 가지였다.
처음에는 키워드가 적어서 비교적 쉬운 주차가 될 것이라고 생각했다. 하지만 실제로 공부해 보니 전혀 그렇지 않았다. 오히려 가장 기본적으로 보이지만, 문제에 따라 가장 어렵게 느껴질 수도 있는 개념이라고 생각했다.
저번 주까지는 알고리즘마다 어느 정도 ‘공식처럼 적용할 수 있는 로직’이 있다고 느꼈다면, 이번 주에 배운 개념들은 단순히 외워서 적용하기보다 문제를 보고 스스로 규칙을 찾아야 한다는 점에서 마치 IQ 테스트를 푸는 느낌에 가까웠다.
시도한 접근 방식
이번 주에는 지난주에 충분히 하지 못했던 시간 복잡도 고려를 하면서 문제를 풀어보려고 했다.
문제를 보자마자 바로 코드부터 작성하기보다는, 어떤 방식으로 접근해야 하는지와 시간 복잡도가 적절한지를 먼저 생각해 보려고 노력했다.
하지만 문제 난이도가 너무 높아서 혼자 힘으로 흐름이 잡히지 않을 때는, 해당 문제에 대한 해설 영상이 있다면 그것을 보면서 원리를 이해하는 식으로 공부했다. 단순히 정답만 보는 것이 아니라, 왜 그런 방식으로 풀리는지 이해하려고 하면서 접근했다.
문제와 해결 과정
이번 주에 가장 많은 시간을 들인 문제는 DP 문제 ‘평범한 배낭’(백준 12865) 이었다.
백준 12865 평범한 배낭
# DP - 평범한 배낭 (백준 골드5)
# 문제 링크: https://www.acmicpc.net/problem/12865
defsolve():# 1. 입력 받기
n,k=map(int,input().split())# 2. dp 테이블 생성: 행(k+1개, 무게 0~k) x 열(n개, 물건 0~n-1)
# 초기값은 모두 0으로 설정
dp=[[0]*nfor_inrange(k+1)]items=[]for_inrange(n):w,v=map(int,input().split())items.append((w,v))fisrt_w,first_v=items[0]iffisrt_w<=k:dp[fisrt_w][0]=first_v# 3. 물건을 하나씩 확인 (열을 하나씩 채워나감)
forbaggageinrange(1,n):current_w,current_v=items[baggage]# [Case 2] 두 번째 물건부터 처리
# Step 1: 이전 열(j-1)의 결과를 현재 열(j)로 그대로 복사
foriinrange(k+1):dp[i][baggage]=dp[i][baggage-1]# Step 2: 밀어내기 (물건을 넣는 경우)
foriinrange(k+1):# 이전 물건까지의 결과(i)에 현재 물건(current_w)을 더했을 때 한도 내라면
ifi+current_w<=k:# 도착지 칸(i + current_w)에 기존 값과 새 값을 비교하여 더 큰 값 이면 가방에 담고 작으면 가방에 안담고
dp[i+current_w][baggage]=max(dp[i+current_w][baggage],dp[i][baggage-1]+current_v)# 4. 결과 출력: 마지막 열(n-1)에 담긴 값들 중 최댓값
# 리스트 컴프리헨션을 사용하여 마지막 열의 값들을 모아 최댓값을 찾습니다.
result=max(dp[i][n-1]foriinrange(k+1))print(result)solve()
코드 자체만 보면 비교적 단순해 보일 수 있지만, 실제로는 그 원리를 이해하는 데 많은 시간이 걸렸다.
이 문제를 풀면서 가장 어려웠던 점은 2차원 DP 테이블을 어떤 기준으로 해석해야 하는지 이해하는 것이었다.
내가 이해한 방식으로 설명하자면, 가방에 현재 물건을 넣지 않는 경우에는 이전 열의 값을 그대로 참고하기 때문에 무게를 더하지 않는다. 반면, 현재 물건을 넣는 경우에는 이전 상태에서 현재 물건의 무게를 더한 위치로 이동하면서 가치를 갱신한다.
즉, 이전까지의 결과를 그대로 가져오는 경우와, 현재 물건을 추가해서 새로운 상태를 만드는 경우를 비교하면서 테이블을 채워 나가는 방식이라고 이해했다.
머리로는 어느 정도 이해했지만, 이것을 2차원 표의 흐름으로 완전히 설명하는 것은 아직도 쉽지 않다. 그래도 이 문제를 오래 붙잡고 고민하면서 DP가 단순한 암기 개념이 아니라, 상태를 어떻게 정의하고 전이시키는지 이해하는 과정이라는 것을 느낄 수 있었다.
새롭게 배운 점
이번 주에는 알고리즘뿐 아니라 리액트를 직접 구현해보는 경험도 할 수 있었다.
그 과정에서 Component, useEffect, useState, useMemo와 같은 핵심 개념들이 어떤 역할을 하는지 조금 더 알게 되었다. 단순히 문법만 보는 것이 아니라, 직접 구현해 보면서 왜 이런 개념이 필요한지 생각해 볼 수 있었다는 점이 의미 있었다.
또 이번 주 발표를 들으면서, 어떤 조는 코덱스에 스킬을 적용하고 하네스 기법을 활용한 방식으로 작업을 진행했다는 점이 인상적이었다.
그 모습을 보며 다음 주에는 나도 최소 기능부터 직접 구현해 보면서, AI나 다른 도구에 끌려다니기보다 내가 흐름을 주도하면서 개념을 더 단단히 잡아보고 싶다는 생각이 들었다.
다음 주 계획
다음 주에는 파이썬이 아니라 C 언어로 자료구조를 구현하게 된다.
아직 C 언어를 제대로 다뤄본 적이 없어서 걱정도 되지만, 새로운 언어를 배우는 만큼 기본 개념을 더 탄탄하게 다질 수 있는 기회가 될 것이라고 생각한다. 두려움이 있지만, 그만큼 더 열심히 해봐야겠다는 생각이 들었다.