220v
젝무의 개발새발
220v
전체 방문자
오늘
어제
  • 분류 전체보기 (255)
    • AI (35)
      • ML, DL 학습 (30)
      • 논문 리뷰 (4)
      • 실습 및 프로젝트 (1)
    • Algorithm (145)
      • LeetCode (13)
      • 프로그래머스 (35)
      • 백준 (96)
      • 알고리즘, 문법 정리 (1)
    • Mobile, Application (17)
      • Flutter (10)
      • iOS, MacOS (7)
    • BackEnd (7)
      • Flask (1)
      • Node.js (5)
      • Spring, JSP..etc (1)
    • Web - FrontEnd (18)
      • JavaScript, JQuery, HTML, C.. (12)
      • React (6)
    • DataBase (1)
      • MySQL (1)
      • Firebase Firestore (0)
      • Supabase (0)
    • Git (1)
    • 기타 툴 및 오류 해결 (3)
    • 강의 (5)
      • Database (3)
      • 암호학 (2)
      • 알고리즘 (0)
    • 후기와 회고 (2)
    • 블로그 꾸미기 (1)
    • 일상과 이것저것 (20)
      • 맛집 (12)
      • 세상사는일 (4)
      • 도서리뷰 (1)
      • 이런저런 생각들 (잡글) (3)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • implementation
  • dp
  • top-down
  • Lis
  • IMPLEMENT
  • binary search
  • BFS
  • 구현
  • disjoint set
  • REACT
  • 위상 정렬
  • 다익스트라
  • 오블완
  • Mathematics
  • topological sort
  • Dynamic Programming
  • Greedy
  • Prefix Sum
  • union-find
  • simulation
  • 프로그래머스
  • Minimum Spanning Tree
  • bitmasking
  • brute-Force
  • dfs
  • 티스토리챌린지
  • Backtracking
  • two pointer
  • Priority Queue
  • 백준

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
220v

젝무의 개발새발

Algorithm/백준

[백준] 2281. 데스노트 - 파이썬

2023. 3. 23. 04:14

https://www.acmicpc.net/problem/2281

 

풀이

DP (Dynamic Programming) 문제.

나는 기본적으로 DP는 bottom-up 방식으로 푸는 것을 선호하고,

풀이도 bottom-up 방식으로 먼저 생각해내곤 하는데,

이 문제는 잘 생각이 나지 않아 시도하다 다시 top-down 방식으로 선회한 문제.

top-down 방식으로 푸는 것이 훨씬 구현하기 수월했다.

 

먼저 기저 상태를 0으로 설정하고,

memoization을 활용해 이미 계산된 dp[index]가 존재하면 (maxNum으로 초기화했으므로, maxNum보다 작으면 값이 담긴 것) 그 값을 return.

dp[index]는 index+1번째의 이름을 '새로운 줄' 에 작성할 때, 남은 공간들의 제곱합의 최소를 저장한다.

이를 위해, 다음 이름을 쓸 remain(남은 공간)이 존재한다면, 가능할 때까지 계속 다음 이름을 쓰고, 불가능하게 된 다음의 index일 때의 이름을 다시 '새로운 줄'에 작성할 때의 제곱합의 최소를 저장하면 된다.

다만 가능할 때까지 계속 이름을 쓰는 과정에서, index가 n까지 도달한 경우는, 마지막 이름까지 작성했다는 뜻이 되고, 마지막 줄의 남은 공간은 제외하므로, 0을 저장하고 break한다.

 

예를 들면, 예시 입출력에서의

[7, 4, 2, 3, 2, 5, 1, 12, 7, 5, 6] 의 길이를 가진 이름 11개가 있다고 하자.

이 때 dp[5]는 6번째 이름, 즉 '길이가 5인 이름'을 '새로운 줄'에 쓸 때의 남은 공간들의 제곱합의 최소이다.

flow를 따라가며 계산해보면,

  1. remain = 20 - 5 = 15. 남은 공간은 15칸.

  2. for문 시작. remain=15 이므로 dp[5] = min(dp[5], 15^2+note(6)), remain = 15 - 1 - 1 = 13.

  3. for문 다음 반복. remain=13 이므로 dp[5] = min(dp[5], 13^2+note(7)), remain = 13 - 12 - 1 = 0.

  4. for문 다음 반복. remain=0 이므로 dp[5] = min(dp[5], 0^2+note(8)), remain = 0 - 7 - 1 = -8.

  5. for문 다음 반복. remain=-8이므로 for문 탈출.

  6. 2~5의 과정 속에서, dp[6], dp[7], dp[8]이 구해짐. 이는 각각 7번째, 8번째, 9번째 이름을 새로운 줄에 쓸 때 남은 공간들의 제곱합의 최소이고, 이러한 과정들 속에서 모든 경우의 수를 훑고 지나가게 됨

    (ex. 1번째 이름의 길이가 7, 2번째 이름의 길이가 4이더라도, 세번째 줄의 길이가 2더라도, 첫 번째 줄에 이 3개의 이름을 모두 적는 것이 최적의 답을 찾을 것이라고 장담할 수 없음. 때로는 공간이 남더라도 다음 줄(새로운 줄)에 적는 것이 남는 공간의 제곱합이 최소가 될 때가 있음. 이것이 이 문제를 Greedy로 풀 수 없는 이유.)

  7. 2~4에서 recursive하게 호출된 note()를 이용해, 7번째, 8번째 이름을 공간이 남음에도 새로운 줄에 쓰는 것이 남은 공간들의 제곱합이 최소인 지를 알 수 있고, 그 중 최소값이 dp[5]에 저장됨.

 

이러한 형식으로, dp[4], dp[3], dp[2], dp[1], dp[0]까지 계산하면, 결국 dp[0]은 "1번째 이름을 새로운 줄에 쓸 때의 남은 공간들의 제곱합의 최소"인 값이 되고, 이것이 우리가 구하려고 하는 답이 된다.

 

n, m = map(int, input().split())

name = [int(input()) for _ in range(n)]

# dp 테이블 초기화, 기저 상태(dp[n]) 설정
maxNum = m*m*n
dp = [maxNum] * (n+1)
dp[n] = 0


def note(index: int):
    if dp[index] < maxNum:
        return dp[index]

    remain = m - name[index]

    for i in range(index+1, n+1):
        if remain >= 0:
            if i == n:
                dp[index] = 0
                break
            dp[index] = min(dp[index], remain*remain+note(i))
            remain -= name[i] + 1

    return dp[index]


print(note(0))

 

    'Algorithm/백준' 카테고리의 다른 글
    • [백준] 1012. 유기농 배추 - 파이썬
    • [백준] 7569. 토마토 - 파이썬
    • [백준] 1107. 리모컨 - 파이썬
    • [백준] 1655. 가운데를 말해요
    220v
    220v
    DGU CSE 20 / Apple Developer Academy @ POSTECH 2nd Jr.Learner.

    티스토리툴바