본문으로 바로가기
제품데스크톱 도구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제품 정보는 현재 검증된 개발 소스를 기준으로 합니다.출시·고도화 중

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

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

구현

이 장에서는 0/1 배낭 문제를 Python으로 구현합니다. 먼저 고른 물건까지 복원하는 전체 표 버전을, 이어서 한 행만 쓰는 버전을 만듭니다. 그다음 실제 부분 수열을 돌려주는 LCS 함수를 작성하고, 마지막으로 한 행 배낭 루틴을 C++, Java, TypeScript로 옮깁니다.

0/1 배낭 문제

물건이 n개 있고 각 물건에는 무게와 가치가 있습니다. 가방에는 최대 capacity만큼의 무게를 담을 수 있습니다. 각 물건은 한 번 담거나 담지 않거나 둘 중 하나("0/1")이며, 담은 물건의 가치 합을 최대로 만들어야 합니다.

  • 상태: best[i][c] = 앞의 i개 물건만 써서 용량 c 안에 담을 수 있는 최대 가치
  • 점화식: 물건 i를 건너뛰거나(best[i-1][c]), 들어간다면 담습니다(best[i-1][c - w] + v). 둘 중 큰 값을 고릅니다.
  • 기저 사례: best[0][c] = 0. 물건이 없으면 가치도 없습니다.

Python: 복원까지 하는 전체 표

def knapsack(weights: list[int], values: list[int], capacity: int) -> tuple[int, list[int]]:
    n = len(weights)
    best = [[0] * (capacity + 1) for _ in range(n + 1)]
    for i in range(1, n + 1):
        w, v = weights[i - 1], values[i - 1]
        for c in range(capacity + 1):
            best[i][c] = best[i - 1][c]
            if w <= c and best[i - 1][c - w] + v > best[i][c]:
                best[i][c] = best[i - 1][c - w] + v
    chosen, c = [], capacity
    for i in range(n, 0, -1):
        if best[i][c] != best[i - 1][c]:
            chosen.append(i - 1)
            c -= weights[i - 1]
    return best[n][capacity], chosen[::-1]


print(knapsack([1, 3, 4, 5], [1, 4, 5, 7], 7))  # (9, [1, 2])

한 줄씩 살펴보면 다음과 같습니다.

  • best는 n + 1행, capacity + 1열입니다. 0행은 모두 0으로 남아 기저 사례 역할을 합니다.
  • Python 리스트는 0부터 세므로 i행은 i - 1번 물건을 뜻합니다. 그래서 w, v를 i - 1로 읽습니다.
  • best[i][c] = best[i - 1][c]는 언제나 가능한 "건너뛰기" 선택입니다.
  • if는 "담기" 선택을 시도합니다. 물건이 들어갈 때만, 그리고 더 좋을 때만 값을 바꿉니다.
  • 표가 다 차면 best[n][capacity]가 최적 가치입니다.
  • 복원은 아래 행에서 위 행으로 올라갑니다. 현재 용량에서 i - 1행과 i행의 값이 다르면 i - 1번 물건을 담았다는 뜻이므로 기록하고 그 무게만큼 용량을 줄입니다.

무게 [1, 3, 4, 5], 가치 [1, 4, 5, 7], 용량 7이면 1번과 2번 물건(무게 3 + 4, 가치 4 + 5 = 9)이 최선입니다. 0번과 3번을 담으면 8에 그칩니다.

Python: 한 행이면 충분하다

i행은 i - 1행만 읽으므로, 순서만 조심하면 리스트 하나로 두 행을 대신할 수 있습니다. 용량을 큰 쪽에서 작은 쪽으로 돌면 best[c - w]를 읽는 시점에 그 칸에는 아직 이전 행의 값이 남아 있습니다. 이것이 각 물건을 한 번만 쓰게 만드는 핵심입니다.

def knapsack_value(weights: list[int], values: list[int], capacity: int) -> int:
    best = [0] * (capacity + 1)
    for w, v in zip(weights, values):
        for c in range(capacity, w - 1, -1):  # 큰 용량부터: 물건마다 한 번만
            best[c] = max(best[c], best[c - w] + v)
    return best[capacity]


print(knapsack_value([1, 3, 4, 5], [1, 4, 5, 7], 7))  # 9

반대로 작은 용량부터 돌면 같은 행 안에서 물건을 다시 쓸 수 있게 되어, 물건마다 개수 제한이 없는 무한 배낭(unbounded knapsack)을 풀게 됩니다. 두 문제의 차이는 반복문 하나의 방향뿐입니다.

Python: 부분 수열까지 돌려주는 LCS

def lcs(a: str, b: str) -> str:
    n, m = len(a), len(b)
    L = [[0] * (m + 1) for _ in range(n + 1)]
    for i in range(n - 1, -1, -1):
        for j in range(m - 1, -1, -1):
            if a[i] == b[j]:
                L[i][j] = L[i + 1][j + 1] + 1
            else:
                L[i][j] = max(L[i + 1][j], L[i][j + 1])
    out, i, j = [], 0, 0
    while i < n and j < m:
        if a[i] == b[j]:
            out.append(a[i])
            i, j = i + 1, j + 1
        elif L[i + 1][j] >= L[i][j + 1]:
            i += 1
        else:
            j += 1
    return "".join(out)


print(lcs("ABCBDAB", "BDCABA"))  # BDAB

이 버전은 L[i][j]를 접미사 a[i:]와 b[j:]에 대해 정의하고 표를 뒤에서부터 채웁니다. 덕분에 복원할 때 (0, 0)에서 앞으로 걸어가며 문자를 원래 순서대로 붙일 수 있고, 마지막에 뒤집을 필요가 없습니다.

C++

#include <algorithm>
#include <iostream>
#include <vector>

long long knapsack(const std::vector<int>& weights, const std::vector<long long>& values, int capacity) {
    std::vector<long long> best(capacity + 1, 0);
    for (size_t i = 0; i < weights.size(); ++i) {
        for (int c = capacity; c >= weights[i]; --c) {
            best[c] = std::max(best[c], best[c - weights[i]] + values[i]);
        }
    }
    return best[capacity];
}

int main() {
    std::cout << knapsack({1, 3, 4, 5}, {1, 4, 5, 7}, 7) << '\n';  // 9
}

가치를 많이 더하면 32비트 int를 넘을 수 있으므로 long long을 씁니다.

Java

public final class Knapsack {
    static long knapsack(int[] weights, long[] values, int capacity) {
        long[] best = new long[capacity + 1];
        for (int i = 0; i < weights.length; i++) {
            for (int c = capacity; c >= weights[i]; c--) {
                best[c] = Math.max(best[c], best[c - weights[i]] + values[i]);
            }
        }
        return best[capacity];
    }

    public static void main(String[] args) {
        System.out.println(knapsack(new int[] {1, 3, 4, 5}, new long[] {1, 4, 5, 7}, 7)); // 9
    }
}

TypeScript

function knapsack(weights: number[], values: number[], capacity: number): number {
  const best = new Array<number>(capacity + 1).fill(0);
  for (let i = 0; i < weights.length; i++) {
    for (let c = capacity; c >= weights[i]; c--) {
      best[c] = Math.max(best[c], best[c - weights[i]] + values[i]);
    }
  }
  return best[capacity];
}

console.log(knapsack([1, 3, 4, 5], [1, 4, 5, 7], 7)); // 9

네 언어 모두 모양이 같습니다. 크기가 capacity + 1인 배열 하나, 물건에 대한 바깥 반복문, 큰 용량에서 작은 용량으로 내려가는 안쪽 반복문입니다.

정리

  • 선택 내용을 복원해야 하면 전체 표를, 값만 필요하면 한 행을 씁니다.
  • 한 행 0/1 배낭에서는 안쪽 반복문이 반드시 큰 용량에서 작은 용량으로 내려가야 합니다.
  • LCS를 접미사로 정의하면 복원을 앞에서부터 할 수 있습니다.
  • 핵심 루틴은 어느 언어에서나 몇 줄이며, C++과 Java에서는 정수 오버플로에 주의합니다.
이 장에 대해 질문이 있나요?
이전 장동작 원리다음 장복잡도

댓글 0개

인기 댓글순최신순

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