ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 백준 1463번 - 1로 만들기 파이썬
    개발/알고리즘 2025. 3. 27. 14:19
    반응형

    📌 백준 1463번 - 1로 만들기 (DP)

    ✨ 문제 요약

    정수 N이 주어졌을 때, 아래 연산 중 하나를 선택해서
    정수 1을 만들기 위한 최소 연산 횟수를 구하는 문제입니다.

    사용할 수 있는 연산

    1. X → X / 3 (단, 3으로 나누어떨어질 때)
    2. X → X / 2 (단, 2로 나누어떨어질 때)
    3. X → X - 1

    🧠 초기 접근 시도 (그리디 → 실패)

    처음에는 아래처럼 단순하게 나눌 수 있을 때마다 나누는 식으로 접근했지만,

    while N > 1:
        if N % 3 == 0:
            N //= 3
        elif N % 2 == 0:
            N //= 2
        else:
            N -= 1

    이 방식은 항상 최적의 해를 보장하지 않습니다.
    예를 들어 N = 10일 때,
    그리디 방식은 10 → 5 → 4 → 2 → 1 (4번)
    최적은 10 → 9 → 3 → 1 (3번)

    DP(동적 계획법)을 적용해야 최적해를 구할 수 있습니다.


    🔧 점화식 설계

    dp[i] = 정수 i를 1로 만들기 위한 최소 연산 횟수

    dp[i] = min(
        dp[i - 1] + 1,              # -1 연산
        dp[i // 2] + 1 (if i % 2),  # /2 연산
        dp[i // 3] + 1 (if i % 3)   # /3 연산
    )

    i는 항상 i-1에서 올 수 있고, 조건에 따라 /2, /3에서도 올 수 있습니다.
    따라서 가능한 모든 경로 중 최소값을 저장합니다.


    🧱 DP 테이블 구성 (1차 테이블)

    i dp[i] 의미 선택 경로
    1 0 1은 연산 필요 없음 -
    2 1 2 → 1 i-1
    3 1 3 → 1 i//3
    4 2 4 → 2 → 1 i//2
    5 3 5 → 4 → 2 → 1 i-1
    6 2 6 → 3 → 1 i//3
    7 3 7 → 6 → 3 → 1 i-1
    8 3 8 → 4 → 2 → 1 i//2
    9 2 9 → 3 → 1 i//3
    10 3 10 → 9 → 3 → 1 i-1

    🧪 DP 갱신 근거 (2차 테이블)

    i dp[i-1]+1 dp[i//2]+1 dp[i//3]+1 dp[i] 선택 경로
    2 1 1 - 1 i-1
    3 2 - 1 1 i//3
    4 2 2 - 2 i-1
    5 3 - - 3 i-1
    6 4 2 2 2 i//3
    7 3 - - 3 i-1
    8 4 3 - 3 i//2
    9 4 - 2 2 i//3
    10 3 - - 3 i-1

    ❌ 내가 처음 작성한 코드의 문제점

    dp = [0] * N         # ❌ 크기 부족 → N+1로 수정 필요
    dp[2] = 1
    dp[3] = 1
    for i in range(4, N+1):
        dp[i] = min(
            dp[i//3]+1 if i % 3 == 0,
            dp[i//2]+1 if i % 2 == 0,
            dp[i] - 1                # ❌ 아직 정의되지 않은 값 사용
        )

    문제점 요약:

    • dp 크기 부족 → N+1 필요
    • dp[i]는 아직 정의되지 않았는데 dp[i] - 1을 사용함
    • 연산 경로 중 무조건 존재하는 -1 연산(dp[i-1]+1)이 빠져 있음

    ✅ 수정된 정답 코드

    N = int(input())
    dp = [0] * (N + 1)
    
    for i in range(2, N + 1):
        dp[i] = dp[i - 1] + 1
        if i % 2 == 0:
            dp[i] = min(dp[i], dp[i // 2] + 1)
        if i % 3 == 0:
            dp[i] = min(dp[i], dp[i // 3] + 1)
    
    print(dp[N])

    🧩 배운 점

    • DP 테이블을 만들 땐 항상 의미와 상태 정의부터 시작하자
    • dp[i]를 채울 때는 이전 상태들을 기반으로 점화식을 세운다
    • 초기값 설정, 배열 크기, 값 정의 순서 등은 실수하기 쉬우니 주의!
    • 1차 테이블 + 2차 테이블을 활용하면 디버깅도 쉽고 논리 흐름도 명확해진다

    🎓 정리

    항목 설명
    문제 유형 DP (최소 연산 횟수)
    점화식 dp[i] = min(dp[i-1]+1, dp[i//2]+1, dp[i//3]+1)
    테이블 1차 테이블: 상태/의미 중심, 2차 테이블: 경로 비교 중심
    디버깅 팁 테이블로 연산 흐름 추적하면 실수 줄일 수 있음

    📘 같은 유형으로 연습하면 좋은 문제

    • 백준 9095 (1,2,3 더하기)
    • 백준 2579 (계단 오르기)
    • 백준 11726 (2xn 타일링)

     

    반응형
Designed by Tistory.