Post

C++: 동적 프로그래밍(Dynamic Programming)

C++: 동적 프로그래밍(Dynamic Programming)

동적 프로그래밍(Dynamic Programming)

  • 이전의 계산 결과를 저장해 이후의 계산결과에서 다시 쓰는 기법
  • 대략 다음과 같은 과정을 통해 최종 결과에 도달한다. 어찌 보면 재귀를 이용하는 브루털 포스와 유사하다고도 볼 수 있지만, 한 번 계산한 적이 있는 값을 다시 계산하는게 아니라 저장해놨다가 다시 사용한다는 점에서 차이가 있다.
    1. 문제가 무엇을 의미하는지 한 문장 수준으로 정의한다.
    2. 점화식, 큰 문제를 작은 문제들로 구성하도록 만든다.
    3. 가장 작은 문제에서의 결과를 나타낼 초기값을 설정한다.
    4. 답을 계산할지 아니면 기존에 계산했던 결과에서 꺼내올지 결정하며 최종 결과를 계산한다.

대표적인 문제

  • 배낭문제

    • 특정 상황에선 그리디 알고리즘으로도 풀리겠지만, 대부분의 경우 DP가 더 효과
      1. 특정 가치와 무게를 가지는 물건이 여러개 있을 때, 배낭에 특정 무게만큼 넣을 수 있다면, 배낭에 넣을 수 있는 최대 가치는?
      • 가치, 무게, 2차원 추적 2. dp[i, w] = max(dp[i-1, w], dp[i-1, w - 무게] + 가치)
      • i번째에 물건을 넣을 때와 안넣을 때 어느 쪽이 더 가치있는지 확인 3. dp[0][w] = 0, dp[i][0] = 0
      • 아무물건도 안넣었을 때 가치 0, 용량이 0이면 가치 0 4. 확실한 내용부터 표 채워넣기, 모든경우의 수 계산보다 빠름!
      • ex 배낭 용량 7kg, 사과 2kg 5$, 복숭아 4kg 4$, 오렌지 3kg 6$, 수박 7kg 18$
    종류0Kg1Kg2Kg3Kg4Kg5Kg6Kg7Kg
    없을때00000000
    (dp[0][w])
    사과00555555
    복숭아00555599
    오렌지00566111111
    수박0
    (dp[i][0])
    0566111118
    • 수박만 넣어 오는게 제일 이득!
  1. 최장 공통 부분수열(Longest Common Substring, LCS)

    • 순서를 지켜 뽑되, 떨어져있어도 되는 수열중 가장 긴 부분수열 찾기
      • Ex) “ABCDE” , “ABEDC” -> “ABD”
    • 배낭 문제처럼 표 채우기로 간단히 풀이 가능
      • 처음 모두 0에서 시작
      • 글자가 같으면 대각선 값 + 1
      • 글자가 다르면 왼쪽이나 위에서 큰 값으로
      • 끝까지 표 채우면 제일 오른쪽 최하단이 LCS 길이!
     시작ABCDE
    시작000000
    A011111
    B012222
    E012223
    D012233
    C012333
  2. 편집거리 계산

    • 특정 문자열을 다른 문자열로 변환하기위해 필요한 삽입/삭제/교체 횟수의 총 합은?
      • ex)sunday -> saturday, 대체 2회, 삽입 1회
    • LCS와 비슷하게 해결가능!
      • 같으면 대각선 그대로 - 편집없음
      • 다르면 1+ min(위, 왼쪽, 대각선) -1회 편집
      • 선택지 총 3
     시작saturday
    시작012345678
    s101234567
    u211223456
    n322233456
    d433334345
    a543444434
    y654455553
    • 총 편집거리 3!
  3. LIS(Longest Increasing Subsequence)

    • 배열중에 증가하는 구간의 총 길이는? (LCS처럼 서로 떨어져있어도 괜찮음)
    • 이전 문제들보다 간단하게 해결 가능
      • dp[0] = 0
      • dp[i] = (앞구간에서 본인보다 작은값중 dp 최대값) + 1
    시작103043156785410032
    DP122
    (더작음)
    3455
    (더작음)
    77
    (더작음, 최종)

활용

  • 범용적으로 활용 가능하며 성능도 준수한 편으로 많은 용도로 사용되곤 한다.
  • git diff - 파일간의 차이에서 공통적인 줄/문자열을 파악하고, 나머지를 +/-로 구분해 차이를 확인한다.
  • 자원배분 문제 - 예산 배정, 생산라인 설계등 한정된 자원을 배분하는 대부분의 문제는 배낭 문제의 변형으로 표현 가능하다.
    • 클라우드 서버 장비 성능 결정
    • 투자 포트폴리오 분배
    • 광고, 팀 예산 분배 등등
  • 게임
    • 인벤토리 배열 조합
    • 스킬 트리 포인트 분배
    • 대사 유사도 검출
    • 스피드런 행동 순서
  • 웹/서버
    • 추천 시스템 유사도(LCS)
    • 광고 예산 배분
    • 방문 패턴 비교분석
  • CS
    • 코딩 테스트에 자주 나오는 개념
    • DNA 서열 정렬(BLAST)
    • 음성 인식(비터비)
    • 맞춤법 및 자동완성 (LCS)
  • 배낭문제, LCS, LIS, 편집거리 문제로 단순화 될 수 있는 많은 경우에 적용 가능하다.
This post is licensed under CC BY 4.0 by the author.