본문으로 바로가기
제품데스크톱 도구TASTECAD도면 확인TASTEFILES파일 관리TASTEFTP파일 전송TASTESSH원격 작업공간TASTEZIP압축과 해제개발 도구TASTECODERust 통합 개발 환경TASTESTUDIO통합 개발·테스트 환경TASTEETL데이터 통합(ETL)서버TASTEREMOTE원격 데스크톱 서버TASTEGEO공간정보 서버TASTEMAIL메일 작업공간시작하기다운로드문서구매모든 제품
마켓플레이스
소식
개발소식오늘의 개발 소식개발 문서프로그래밍 언어RustTypeScriptJavaScriptPythonGoC#JavaCC++KotlinDart프런트엔드 프레임워크Next.jsReactVue.js · NuxtSvelteKitAngular서버 프레임워크Spring BootDjangoFastAPIASP.NET CoreNestJS · Express앱 · 데스크톱 프레임워크TauriFlutterReact NativeElectron알고리즘알고리즘 기초알고리즘 핵심알고리즘 심화실무 알고리즘
커뮤니티프로젝트개발 로그프롬프트AI 도구 이슈쇼케이스커뮤니티 안내
협업기술 전수홈페이지 구축데스크톱 앱 구축릴리스·결제 파이프라인판매 파트너문의
서비스원격 데스크톱설치 패키지 커스텀
TASTEDEV
제품

데스크톱 도구

TASTECAD복잡한 도면을 가볍게 열다TASTEFILES필요한 파일만, 가장 빠르게TASTEFTP파일이 오가는 가장 단정한 길TASTESSH서버와 나 사이, 더 단단한 연결TASTEZIP더 빠르게 묶고, 더 안전하게 풀다

개발 도구

TASTECODERust 앱을 편집 · 빌드 · 디버깅 · 테스트까지 한 창에서TASTESTUDIO편집부터 원격 테스트와 승인 기반 AI 수정까지TASTEETL값을 보며 데이터를 옮깁니다

서버

TASTEREMOTE우리 회사 서버로 운영하는 원격 데스크톱TASTEGEOGeoServer 호환 공간정보 서버를 Rust로 다시 세웁니다TASTEMAIL메일의 주도권을 다시 내 손에

시작하기

다운로드모든 제품 최신 판문서제품 사용법구매구독 · 영구 라이선스모든 제품모든 제품 한눈에
모든 제품
마켓플레이스소식
개발

소식

오늘의 개발 소식언어 · 프레임워크 · GitHub · AI 공식 발표개발 문서언어 · 프레임워크 · 알고리즘 안내서

프로그래밍 언어

RustTypeScriptJavaScriptPythonGoC#JavaCC++KotlinDart

프레임워크

프런트엔드 프레임워크Next.jsReactVue.js · NuxtSvelteKitAngular
서버 프레임워크Spring BootDjangoFastAPIASP.NET CoreNestJS · Express
앱 · 데스크톱 프레임워크TauriFlutterReact NativeElectron

알고리즘

알고리즘 기초복잡도 분석 · 정렬 · 탐색 · 기본 자료구조 · 트리와 힙알고리즘 핵심그래프 탐색 · 최단 경로와 최소 신장 트리 · 동적 계획법 · 탐욕 알고리즘 · 분할 정복 · 백트래킹알고리즘 심화문자열 알고리즘 · 수학과 정수론 · 비트 연산 · 구간 트리 · 유니온 파인드실무 알고리즘코딩 면접 풀이 전략 · LRU 캐시 · 레이트 리미터 · 일관된 해시
개발 문서 전체 보기
커뮤니티
프로젝트빌더와 작업 발견개발 로그결정과 시행착오프롬프트재사용 가능한 지시AI 도구 이슈Claude · Codex · Gemini 문제와 우회법쇼케이스완성된 결과커뮤니티 안내공개하고 함께 만드는 방식
협업
기술 전수Rust·egui 네이티브 앱 만들기홈페이지 구축10개 언어, 계정, 문서·다운로드데스크톱 앱 구축Rust·egui, WebView 없이릴리스·결제 파이프라인서명 · MSI·deb·rpm·AppImage · 해시판매 파트너소개하고 결제액의 30% 받기문의구축 · 제휴 · 투자 문의
서비스
원격 데스크톱브라우저로 내 컴퓨터에 접속설치 패키지 커스텀필요한 기능만, 설정은 미리
구매
KO
한국어EnglishDeutschEspañolFrançaisItalianoPortuguês (Brasil)日本語简体中文繁體中文
로그인
로그인
TASTEDEV

작은 도구부터, 더 좋은 개발까지.

support@tastedev.netTASTEDEV 후원하기
제품제품군구매문서다운로드마켓플레이스고객 지원
커뮤니티프로젝트개발 로그프롬프트AI 도구 이슈쇼케이스
정책개인정보처리방침이용약관구독 약관환불 정책커뮤니티 운영 정책
계정로그인가입하기
방문자
오늘
012345678901234567890123456789012345678901234567890123456789
어제
012345678901234567890123456789012345678901234567890123456789
누적 조회
0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
상호
지엑스소프트
대표자
최영구
사업자등록번호
853-44-00368
주소
인천광역시 미추홀구 경인로 351 9층 V24
전화
+82-10-4404-1523
이메일
whitecalvin@tastedev.net
개인정보 보호책임자
최영구

© 2026 TASTEDEV제품 정보는 현재 검증된 개발 소스를 기준으로 합니다.출시·고도화 중

동적 계획법 문서
동적 계획법
개념동작 원리구현복잡도연습 문제실무 활용
토론

동적 계획법 안내서 · 4/6

복잡도

DP의 복잡도 분석은 대체로 기계적입니다. 상태 수를 세고, 상태 하나당 하는 일을 세고, 두 값을 곱합니다. 이 장에서는 이 규칙을 고전 문제에 적용하고, 배낭 문제를 왜 의사 다항 시간이라고 부르는지, 메모이제이션과 타뷸레이션을 어떻게 비교하는지, 그리고 롤링 배열로 메모리를 줄이는 방법을 설명합니다.

기본 규칙

  • 시간 = 상태 수 x 전이 하나의 비용
  • 공간 = 동시에 메모리에 들고 있는 상태 수

피보나치는 상태가 n + 1개이고 각각 상수 시간이므로 O(n)입니다. LCS는 상태가 (n + 1)(m + 1)개이고 각각 상수 시간이므로 O(nm)입니다. 동전 종류가 k개인 동전 교환은 amount + 1개의 상태가 각각 동전 k개를 살피므로 O(amount x k)입니다.

고전 문제 한눈에 보기

문제상태시간공간(표)공간(최적화)
피보나치nO(n)O(n)O(1)
동전 교환(최소 개수)금액O(A x k)O(A)O(A)
0/1 배낭물건, 용량O(n x W)O(n x W)O(W)
LCS / 편집 거리i, jO(n x m)O(n x m)O(min(n, m))
LIS, 단순 DP끝 위치O(n^2)O(n)O(n)
LIS, 이분 탐색길이O(n log n)O(n)O(n)

DP에는 최선과 최악의 경우가 따로 있는 일이 드뭅니다. 입력 값과 상관없이 표를 끝까지 채우기 때문입니다. 다만 메모이제이션은 시작 상태에서 도달하는 상태가 일부뿐일 때 실제로 더 빠를 수 있습니다.

의사 다항 시간

배낭 문제의 O(n x W)는 다항식처럼 보이지만, W는 입력의 길이가 아니라 수의 크기입니다. W를 적는 데는 약 log2(W)비트면 충분하므로 입력에 비트 하나만 더해도 실행 시간이 두 배가 될 수 있습니다. 이런 알고리즘을 의사 다항 시간(pseudo-polynomial) 알고리즘이라고 합니다. 0/1 배낭 문제는 일반적으로 NP-난해이며, DP는 용량이 적당히 작은 정수일 때만 실용적입니다. W가 매우 크고 n이 40개 정도로 작다면 반씩 나누어 열거하는 meet-in-the-middle 방식이 더 나은 경우가 많습니다.

메모이제이션과 타뷸레이션 비교

항목메모이제이션(하향식)타뷸레이션(상향식)
코드 모양재귀 함수 + 캐시표를 도는 반복문
계산하는 상태도달 가능한 상태만모든 상태
계산 순서자동으로 정해짐직접 정해야 함
부가 비용함수 호출, 인자 해싱배열 인덱싱
재귀 깊이제한에 걸릴 수 있음(Python 기본 약 1000)재귀 없음
공간 최적화어려움자연스러움(행 굴리기)

Python에서 가장 흔히 놀라는 부분은 재귀 제한입니다. 상태가 수천 개 이어진 메모이제이션 함수는 RecursionError를 내고, sys.setrecursionlimit으로 한도를 올려도 해결되지 않을 수 있습니다. 최근 Python은 C 코드를 거치는 호출의 깊이도 따로 제한하는데, functools.cache 래퍼가 C로 작성되어 있기 때문입니다. 간단한 우회 방법은 작은 값부터 차례로 호출해 캐시를 미리 채우는 것입니다. 그러면 각 호출이 필요한 작은 상태를 이미 캐시에서 찾습니다.

from functools import cache


@cache
def ways(n: int) -> int:
    """1칸 또는 2칸씩 n칸 계단을 오르는 방법의 수"""
    if n <= 1:
        return 1
    return ways(n - 1) + ways(n - 2)


try:
    ways(5_000)
except RecursionError:
    print("too deep")

for i in range(5_001):  # 미리 채우기: 호출마다 재귀가 한 단계뿐
    ways(i)
print(ways(5_000) % 1_000_000_007)
print(ways.cache_info().currsize)  # 상태 5001개 저장

미리 채우기는 사실상 상향식 계산을 돌려서 한 것입니다. 상태가 수만 개라면 처음부터 반복문으로 작성합니다.

롤링 배열

표의 한 행이 바로 이전 행에만 의존한다면 두 행 이상을 들고 있을 필요가 없습니다. 다음은 LCS 길이를 O(min(n, m)) 공간으로 구하는 코드입니다.

def lcs_length(a: str, b: str) -> int:
    if len(b) > len(a):
        a, b = b, a  # 짧은 쪽을 b로
    prev = [0] * (len(b) + 1)
    for ch in a:
        cur = [0] * (len(b) + 1)
        for j, bj in enumerate(b, start=1):
            cur[j] = prev[j - 1] + 1 if ch == bj else max(prev[j], cur[j - 1])
        prev = cur
    return prev[-1]


print(lcs_length("ABCBDAB", "BDCABA"))  # 4

대신 두 행만 남기면 표를 거꾸로 따라가 부분 수열을 복원할 수 없습니다. Hirschberg 알고리즘은 분할 정복을 이용해 선형 공간에서 복원까지 해내며, 시간은 대략 두 배가 듭니다.

상태 정의를 바꿔 더 빠르게

상태를 더 잘 정의하면 복잡도 등급 자체가 바뀌기도 합니다. 단순한 LIS 점화식은 앞의 모든 위치를 살피므로 O(n^2)입니다. 길이마다 "가능한 가장 작은 끝 값"을 들고 있으면 안쪽 반복문을 이분 탐색으로 바꿀 수 있습니다.

from bisect import bisect_left


def lis_length(nums: list[int]) -> int:
    tails: list[int] = []  # tails[k] = 길이 k + 1인 증가 수열의 가장 작은 끝 값
    for x in nums:
        k = bisect_left(tails, x)
        if k == len(tails):
            tails.append(x)
        else:
            tails[k] = x
    return len(tails)


print(lis_length([3, 1, 4, 1, 5, 9, 2, 6]))  # 4, 예: 1, 4, 5, 9

tails는 늘 정렬된 상태를 유지하므로 원소 하나에 O(log n), 전체는 O(n log n)입니다.

다른 접근과 비교

접근배낭류 문제의 일반적 비용정확한가
모든 부분집합 완전 탐색O(2^n)정확하지만 작은 n에서만 가능
가치/무게 비율 그리디O(n log n)0/1 배낭에서는 틀림(분할 가능 배낭에서는 정확)
동적 계획법O(n x W)W가 작을 때 정확
분기 한정최악은 지수, 실제로는 빠른 편정확

정리

  • 시간은 상태 수 x 전이 비용, 공간은 동시에 들고 있는 상태 수입니다.
  • 배낭 DP는 W의 수 크기에 의존하므로 의사 다항 시간입니다.
  • 메모이제이션은 도달하는 상태만 계산하지만 재귀 비용을 치르고, 타뷸레이션은 예측 가능하며 최적화하기 쉽습니다.
  • 롤링 배열은 메모리를 한두 행으로 줄이는 대신 복원을 포기합니다.
이 장에 대해 질문이 있나요?
이전 장구현다음 장연습 문제

댓글 0개

인기 댓글순최신순

로그인 · 로그인하면 댓글을 남길 수 있습니다.