Skip to content

Conversation

@mj010504
Copy link
Collaborator

πŸ”— 문제 링크

μ™„μ „ 범죄
https://school.programmers.co.kr/learn/courses/30/lessons/389480

βœ”οΈ μ†Œμš”λœ μ‹œκ°„

40λΆ„

✨ μˆ˜λ„ μ½”λ“œ

  • 이 문제의 λͺ©ν‘œλŠ” a와 b도둑이 λͺ¨λ“  물건을 ν›”μΉ˜κ³ , a 도둑이 물건을 ν›”μΉ  λ•Œ μ΅œμ†Œν•œμ˜ 흔적을 λ‚¨κΈ°λ©΄μ„œ, a와 b도둑 λͺ¨λ‘ 경찰에 μž‘νžˆμ§€ μ•ŠλŠ” κ²ƒμž…λ‹ˆλ‹€.
  • λͺ¨λ“  물건을 ν›”μ³μ•Όν•˜λ―€λ‘œ DFS λ°©μ‹μœΌλ‘œ ν’€μ—ˆμŠ΅λ‹ˆλ‹€. ν•˜μ§€λ§Œ μ΄λ²ˆμ—λ„ μ‹œκ°„μ΄ˆκ³Όκ°€ λ‚˜κ³  λ§μ•˜μŠ΅λ‹ˆλ‹€.. μ΅œμ•…μ˜ 경우 μ‹œκ°„ λ³΅μž‘λ„λŠ” O(2^40)이기 λ•Œλ¬Έμž…λ‹ˆλ‹€. 참고둜 AIμ—κ²Œ λ¬Όμ–΄λ³΄λ‹ˆ λŒ€λž΅ 2의 25승 μ―€λΆ€ν„° μ‹œκ°„μ΄ˆκ³Όκ°€ λ‚œλ‹€κ³  ν•©λ‹ˆλ‹€.
  • 보톡 DFS둜 μ‹œκ°„μ΄ˆκ³Όκ°€ λ‚ λ•ŒλŠ” μ‹œκ°„μ΄ˆκ³Όλ₯Ό λ°©μ§€ν•˜κΈ° μœ„ν•΄μ„  μ€‘λ³΅λœ 경둜λ₯Ό μ—¬λŸ¬ 번 νƒμƒ‰ν•˜λŠ” 것을 λ°©μ§€ν•΄μ•Όν•©λ‹ˆλ‹€. 즉 DPλ₯Ό μ‚¬μš©ν•˜κ±°λ‚˜ DFS + λ©”λͺ¨μ΄μ œμ΄μ…˜ λ°©μ‹μœΌλ‘œ 풀이가 κ°€λŠ₯ν•  것 κ°™μŠ΅λ‹ˆλ‹€. μ €λŠ” DFS + λ©”λͺ¨μ΄μ œμ΄μ…˜ 방법을 μ‚¬μš©ν–ˆμŠ΅λ‹ˆλ‹€. 이 λ¬Έμ œμ—μ„œ μ€‘λ³΅λœ κ²½λ‘œλž€ (aκ°€ 남긴 흔적 총합, bκ°€ 남긴 흔적 총합, ν˜„μž¬κΉŒμ§€ ν›”μΉœ 물건의 index)κ°€ κ°™μœΌλ©΄ μ€‘λ³΅λœ 경둜둜 κ°„μ£Όν•˜κ³  μ €λŠ” set<tuple<int,int,int>>λ₯Ό μ΄μš©ν•˜μ—¬ μ€‘λ³΅λœ κ²½λ‘œλŠ” 더 νƒμƒ‰ν•˜λŠ” 것을 λ°©μ§€ν•˜μ—¬ ν•΄κ²°ν–ˆμŠ΅λ‹ˆλ‹€.
  • μ€‘λ³΅λœ κ²½λ‘œκ°€ λ°œμƒν•œλ‹€λŠ”κ²Œ μ²˜μŒμ— 이해가 μ•ˆκ°ˆ 수 μžˆλŠ”λ° 물건을 κ³„μ†ν•΄μ„œ ν›”μΉ˜λ‹€λ³΄λ©΄ a와 bκ°€ ν›”μΉœ 물건듀이 각각 λ‹€λ₯΄λ”라도 각각 남긴 ν”μ μ˜ 총합 및 indexλŠ” 같을 수 μžˆμœΌλ―€λ‘œ μ€‘λ³΅λœ κ²½λ‘œκ°€ λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

πŸ“š μƒˆλ‘­κ²Œ μ•Œκ²Œλœ λ‚΄μš©

  • ν”„λ‘œκ·Έλž˜λ¨ΈμŠ€μ— 쒋은 λ¬Έμ œλ“€μ΄ 생각보닀 λ§Žμ€ 것 κ°™μŠ΅λ‹ˆλ‹€!

Copy link
Collaborator

@Seol-Munhyeok Seol-Munhyeok left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DPλ₯Ό μ—°μŠ΅ν•  수 μžˆλŠ” 쒋은 λ¬Έμ œλ„€μš”.

일단 N=40μ΄λΌμ„œ 완전탐색은 λΆˆκ°€λŠ₯ν•˜κ³  그림을 λͺ‡ 개 κ·Έλ €λ³΄λ‹ˆ DFS둜 μ²˜λ¦¬κ°€ 될 κ±° κ°™μ•˜μŠ΅λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ λ‹¨μˆœ DFS둜만 ν•˜λ©΄ μ—­μ‹œ μ‹œκ°„ μ΄ˆκ³Όμ΄λ―€λ‘œ μ—¬κΈ°μ„œ λ©”λͺ¨μ΄μ œμ΄μ…˜μ„ μ‚¬μš©ν•΄μ„œ μ΅œμ ν™”λ₯Ό ν•΄μ•Όν•  κ±° κ°™μ•˜μŠ΅λ‹ˆλ‹€. (14μ£Όμ°¨ PRμ—μ„œμ˜ κ²½ν—˜μ΄ 도움이 λ˜μ—ˆμŠ΅λ‹ˆλ‹€.)

그런데 μƒνƒœλ₯Ό μ–΄λ–»κ²Œ μ •μ˜ν•΄μ•Όν• μ§€ 도무지 생각이 λ‚˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€. λ­”κ°€ λ°°λ‚­ 문제 같은 λŠλ‚Œμ΄κΈ΄ ν•œλ° A와 B 두 개의 경우λ₯Ό λͺ¨λ‘ κ³ λ €ν•΄μ•Όν•˜λ‹ˆ μ–΄λ €μ› μŠ΅λ‹ˆλ‹€.

κ·Έλž˜μ„œ μ•„λž˜ μ„€λͺ…은 μ œκ°€ μƒκ°ν•œ 게 μ•„λ‹Œ ChatGPTλ₯Ό 참고함을 λ°νž™λ‹ˆλ‹€.

점화식

$dp[i][a] =$ μ•žμ—μ„œ $i$개 μ²˜λ¦¬ν–ˆκ³ , A의 λˆ„μ  흔적이 $a$일 λ•Œ 달성 κ°€λŠ₯ν•œ B의 흔적 μ΅œμ†Œ ν•©

  • μ‹œμž‘: $dp[0][0] = 0$, λ‚˜λ¨Έμ§€λŠ” INF(= $m$ 이상)둜 채움
  • 전이(물건 $i$의 정보가 $(a_i, b_i)$일 λ•Œ):
    • $i$ λ₯Ό Aκ°€ ν›”μΉ˜λ©΄: $dp[i+1][a + a_i] = min(dp[i+1][a + a_i], dp[i][a])$
    • $i$ λ₯Ό Bκ°€ ν›”μΉ˜λ©΄: $dp[i+1][a] = min(dp[i+1][a], dp[i][a] + b_i)$
  • λ²”μœ„/κ°€μ§€μΉ˜κΈ°:
    • AλŠ” $a &lt; n$만 μœ μ§€ ($a$κ°€ $n$ 이상이면 Aκ°€ μž‘νžˆλ―€λ‘œ 버림)
    • BλŠ” 값이 $m$ 이상이면 더 λ³Ό ν•„μš” μ—†μŒ(작힘) β†’ κ°±μ‹  μŠ€ν‚΅
  • μ΅œμ’… λ‹΅ : $i = N$μ—μ„œ $a = 0..n-1$ 쀑 $dp[N][a] &lt; m$인 κ°€μž₯ μž‘μ€ $a$
    • μ—†μœΌλ©΄ : -1

μƒνƒœ 트리

  • μ˜ˆμ‹œ κ°’ : info = [(1,2), (3,1), (2,2)], n=5, m=4
  • λ…Έλ“œ 라벨: (i, a) [μ΅œμ†Œ Bν•©]
(0,0)[0]
β”œβ”€ A:+1 β†’ (1,1)[0]
β”‚   β”œβ”€ A:+3 β†’ (2,4)[0]
β”‚   β”‚   β”œβ”€ A:+2 β†’ (3,6)[0]   βœ— μ»· (a=6β‰₯5)
β”‚   β”‚   └─ B:+2 β†’ (3,4)[2]   β—€ 후보
β”‚   └─ B:+1 β†’ (2,1)[1]
β”‚       β”œβ”€ A:+2 β†’ (3,3)[1]   β—€ 후보
β”‚       └─ B:+2 β†’ (3,1)[3]   β—€ 후보
└─ B:+2 β†’ (1,0)[2]
    β”œβ”€ A:+3 β†’ (2,3)[2]
    β”‚   β”œβ”€ A:+2 β†’ (3,5)[2]   βœ— μ»· (a=5β‰₯5)
    β”‚   └─ B:+2 β†’ (3,3)[4]   βœ— μ»· (b=4β‰₯4)
    └─ B:+1 β†’ (2,0)[3]
        β”œβ”€ A:+2 β†’ (3,2)[3]   β—€ 후보
        └─ B:+2 β†’ (3,0)[5]   βœ— μ»· (b=5β‰₯4)
  • λ¦¬ν”„λŠ” $i=3$(λͺ¨λ“  물건 처리)인 λ…Έλ“œλ“€.
  • 살아남은 λ¦¬ν”„λ“€μ˜ $(a, b)$λŠ”:
    (3,4)[2], (3,3)[1], (3,1)[3], (3,2)[3]
  • μ—¬κΈ°μ„œ $b &lt; m(=4)$을 λ§Œμ‘±ν•˜λŠ” 리프듀 쀑 $a$κ°€ μ΅œμ†ŒμΈ 게 λ‹΅ β†’ $a=1$이 μ΅œμ†Ÿκ°’.

μ½”λ“œ

def solution(info, n, m):
    INF = int(1e9)
    N = len(info)
    capA = min(n-1, 3*N)  # A 흔적 ν•© μƒν•œ
    # dp[i][a] = μ•žμ—μ„œ i개 μ²˜λ¦¬ν–ˆκ³ ,
    # A의 λˆ„μ  흔적이 a일 λ•Œ 달성 κ°€λŠ₯ν•œ B의 흔적 μ΅œμ†Œ ν•©
    dp = [[INF for _ in range(capA + 1)] for _ in range(N + 1)]
    dp[0][0] = 0
    
    for i in range(N):
        a_i, b_i = info[i][0], info[i][1]
        for a in range(capA + 1):
            if dp[i][a] >= INF:
                continue
            # iλ₯Ό Aκ°€ ν›”μΉ˜λŠ” 경우
            na = a + a_i
            if na < n and na <= capA:
                dp[i + 1][na] = min(dp[i + 1][na], dp[i][a])
            # iλ₯Ό Bκ°€ ν›”μΉ˜λŠ” 경우
            nb_sum = dp[i][a] + b_i
            if nb_sum < m:
                dp[i + 1][a] = min(dp[i + 1][a], nb_sum)
    
    answer = INF
    for a in range(capA + 1):
        if dp[N][a] < m:
            answer = min(answer, a)

    return answer if answer != INF else -1

μ†Œκ°

μ œκ°€ 문제 풀이에 어렀움을 느꼈던 μ΄μœ λŠ” ν˜„μž¬ $i$번째 물건을 ν›”μΉ˜λŠ” μ‚¬λžŒμ΄ λˆ„κ΅¬μΈκ°€μ— λ”°λΌμ„œ μƒνƒœλ₯Ό λ‚˜λˆ„λ €κ³  ν•΄μ„œμ˜€μŠ΅λ‹ˆλ‹€. μš°λ¦¬κ°€ κ΅¬ν•˜λŠ” 건 A도둑이 남긴 ν”μ μ˜ λˆ„μ  개수의 μ΅œμ†Ÿκ°’ 이지, λˆ„κ°€ μ–΄λ–€ κ±Έ ν›”μ³€λŠ”μ§€λŠ” μ „ν˜€ μ€‘μš”ν•˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ—, ν”μ μ˜ λˆ„μ  개수 기반으둜 μƒκ°ν–ˆμ–΄μ•Ό ν–ˆμŠ΅λ‹ˆλ‹€.

또, ν”μ μ˜ λ²”μœ„κ°€ μ΅œλŒ€ 3μ΄λΌλŠ” 것도 큰 νžŒνŠΈμ˜€λŠ”λ° λ§Œμ•½ 3보닀 더 크닀면 dp λ°°μ—΄μ˜ 크기도 더 컀지기 λ•Œλ¬Έμ— ν’€ 수 μ—†μœΌλ―€λ‘œ μ΄λŠ” 문제 μ œμž‘μžκ°€ μ˜λ„ν•œ 뢀뢄이라 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.

DP 문제λ₯Ό ν’€ λ•Œ, μ–΄λ–€ λ³€μˆ˜λ₯Ό μΆ•μœΌλ‘œ 삼을 것인가(DP ν…Œμ΄λΈ”μ„ μ–΄λ–€ λ³€μˆ˜λ₯Ό κΈ°μ€€μœΌλ‘œ λ°°μ—΄μ˜ μ°¨μ›μœΌλ‘œ μ‚ΌλŠλƒ)λ₯Ό μ •ν•  λ•Œ, μ œμ•½μ΄ 적은 μͺ½μ„ μΆ•μœΌλ‘œ μ‚ΌλŠ” 것이 μ’‹λ‹€κ³ ν•©λ‹ˆλ‹€.
이 λ¬Έμ œμ—μ„œλŠ”

  • A μ œμ•½: A의 흔적 ν•© $&lt; n$
  • B μ œμ•½: B의 흔적 ν•© $&lt; m$

이 λ˜λŠ”λ° 이 λ¬Έμ œλŠ” 두 μ œμ•½μ˜ 크기가 κ°™μœΌλ―€λ‘œ μ–΄λŠ μͺ½μ„ μΆ•μœΌλ‘œ μž‘μ•„λ„ λΉ„μŠ·ν•˜κ²Œ ν’€λ¦½λ‹ˆλ‹€.


κ°„λ‹¨νžˆ ν’€λ¦¬λ˜ DP λ¬Έμ œμ™€λŠ” 달리 점화식 μ°ΎκΈ°λΆ€ν„° μ‹œμž‘ν•΄μ„œ λ²”μœ„ λ“± 신경써야 ν•  점이 λ§Žμ€ 문제인 κ±° κ°™μŠ΅λ‹ˆλ‹€. λ‚˜μ€‘μ— λ”°λ‘œ ν•΄μ„€ 보지 μ•Šκ³  μŠ€μŠ€λ‘œλ„ ν•œ 번 ν’€μ–΄λ΄μ•Όκ² μŠ΅λ‹ˆλ‹€.

λ―Όμ€€ λ‹˜ ν’€μ΄μ²˜λŸΌ DFS + λ©”λͺ¨μ΄μ œμ΄μ…˜μœΌλ‘œλ„ ν’€ 수 있고 λ‹€λ₯Έ μ‚¬λžŒλ“€ 풀이λ₯Ό μ‚΄νŽ΄λ³΄λ˜ 쀑 κ·Έλ¦¬λ””λ‘œλ„ ν’€ 수 μžˆλŠ” 것 κ°™μŠ΅λ‹ˆλ‹€. (ν™•μ‹€ν•˜μ§€λŠ” μ•ŠμŠ΅λ‹ˆλ‹€) λ‚˜μ€‘μ— λ‹€μ‹œ ν’€ λ•ŒλŠ” λ‹€λ₯Έ ν’€μ΄λ²•μœΌλ‘œλ„ ν’€μ–΄λ΄μ•Όκ² μŠ΅λ‹ˆλ‹€.

쒋은 λ¬Έμ œμ˜€μŠ΅λ‹ˆλ‹€. μˆ˜κ³ ν•˜μ…¨μŠ΅λ‹ˆλ‹€!

@hyeokbini
Copy link
Collaborator

저도 DPλž‘ DFS 쀑 μ–΄λ–€ 방법을 μ‚¬μš©ν• κΉŒ ν•˜λ‹€κ°€, DPλ₯Ό μ‚¬μš©ν•˜λ©΄ 냅색 문제 λΉ„μŠ€λ¬΄λ¦¬ν•˜κ²Œ 풀이가 λ“€μ–΄κ°ˆ 것 κ°™μ•„μ„œ ν•œ 수 μ ‘κ³  DFS + 경둜 λ©”λͺ¨μ΄μ œμ΄μ…˜ 방법을 μ„ νƒν–ˆμŠ΅λ‹ˆλ‹€. 냅색은 μ•Œκ³ λ¦¬μ¦˜μ„ μ•Œμ•„λ„ 손을 λŒ€κΈ°κ°€ λ¬΄μ„­λ„€μš”..

경둜 λ©”λͺ¨μ΄μ œμ΄μ…˜ μ•„μ΄λ””μ–΄λŠ” 3차원 intν˜• dpλ₯Ό μ„ μ–Έν•΄μ€€ ν›„, [물건 인덱슀] [A 흔적 개수] [B 흔적 개수] μΈλ±μŠ€μ— A 흔적 개수 정보λ₯Ό λ‹΄μ•˜μŠ΅λ‹ˆλ‹€. 인덱슀λ₯Ό μ¦κ°€μ‹œμΌœ μ£Όλ©΄μ„œ Aκ°€ ν›”μΉœ 경우, Bκ°€ ν›”μΉœ 경우 2κ°€μ§€ κ²½μš°μ— λŒ€ν•œ λ°©ν–₯으둜 μž¬κ·€ 깊이λ₯Ό μ¦κ°€μ‹œν‚€κ³ , κΈ°μ € 쑰건에 닿이면 ν˜„μž¬ A 흔적값을 returnν•©λ‹ˆλ‹€. 그리고 2개 λ°©ν–₯의 μž¬κ·€ 연산이 λλ‚˜λ©΄, 두 κ°’λ“€ 쀑 더 μž‘μ€ 값을 dp[ν˜„μž¬ 물건 인덱슀][A][B]에 μ €μž₯ν•˜λŠ” 방식을 μ‚¬μš©ν–ˆμŠ΅λ‹ˆλ‹€.

제좜 μ½”λ“œμž…λ‹ˆλ‹€.

완전범죄/c++
#include <bits/stdc++.h>
using namespace std;

int amax, bmax;
vector<vector<int>> v;
int dp[101][101][101];

int solve(int i, int a, int b) {
    // Aλ‚˜ Bκ°€ μž„κ³„μΉ˜ 이상이면 κ°€μ§€μΉ˜κΈ°
    if (a >= amax || b >= bmax) return INT_MAX;

    // λͺ¨λ“  물건 처리 μ™„λ£Œ β†’ ν˜„μž¬ A 흔적이 κ²°κ³Ό
    if (i == (int)v.size()) return a;

    // 이미 κ³„μ‚°λœ μƒνƒœλ©΄ λ°”λ‘œ λ°˜ν™˜
    if (dp[i][a][b] != -1) return dp[i][a][b];

    // 선택 1: Aκ°€ ν›”μΉ¨
    int takeA = solve(i + 1, a + v[i][0], b);

    // 선택 2: Bκ°€ ν›”μΉ¨
    int takeB = solve(i + 1, a, b + v[i][1]);

    // 두 경우 쀑 A 흔적이 더 μž‘μ€ 경우 μ €μž₯
    dp[i][a][b] = min(takeA, takeB);
    return dp[i][a][b];
}

int solution(vector<vector<int>> info, int n, int m) {
    amax = n;
    bmax = m;
    v = info;
    memset(dp, -1, sizeof(dp));

    int ans = solve(0, 0, 0);
    if (ans == INT_MAX) return -1;
    return ans;
}

κ·Έλ¦¬λ””ν•œ λ°©μ‹μœΌλ‘œ ν’€ 수 μžˆλ‹€κ³ λ„ ν•˜λŠ”λ°, 이 방법은 ꡬ글링해 λ³΄λ‹ˆ μ •ν•΄κ°€ μ•„λ‹ˆκ³  ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€κ°€ λΆ€μ‹€ν•΄μ„œ ν†΅κ³Όλœ μ½”λ“œμΌμˆ˜λ„ μžˆλ‹€κ³  ν•©λ‹ˆλ‹€. μ—¬κΈ°λ₯Ό μ°Έκ³ ν•΄λ³΄μ‹œλ©΄ 쒋을 것 κ°™μŠ΅λ‹ˆλ‹€. 문제 ν‘Έμ‹œλŠλΌ κ³ μƒν•˜μ…¨μ–΄μš”!

Copy link
Collaborator

@flydongwoo flydongwoo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

μœΌμ•„ λ¦¬λ·°λ³΄λ‹ˆκΉŒ λ‹€λ“€ DPκ°€ μ•„λ‹Œ DFS둜 ν‘Έμ…¨λ˜λ° μ €λŠ” 2차원 DP둜 ν•΄κ²°ν–ˆμŠ΅λ‹ˆλ‹€!

d[j] = B의 흔적 합이 j(<m)일 λ•Œμ˜ A의 μ΅œμ†Œν•©μ„ κ΅¬ν•˜λŠ” μƒνƒœμž…λ‹ˆλ‹€.
Aκ°€ ν›”μΉ˜λ©΄ j κ·ΈλŒ€λ‘œ +a_i, Bκ°€ ν›”μΉ˜λ©΄ j+b_i(<m)둜 μ΄λ™ν•˜λ©° A 증가 μ—†μœΌλ―€λ‘œ
min_j dp[j]이 A μ΅œμ†Œ. 이 값이 n 이상이면 λΆˆκ°€λŠ₯ν•©λ‹ˆλ‹€.

#include <vector>
#include <algorithm>
#include <limits>
using namespace std;

int solution(int n, int m, vector<vector<int>> info) {
    const int INF = numeric_limits<int>::max() / 4;
    vector<int> dp(m, INF), ndp(m, INF);
    dp[0] = 0;

    for (size_t i = 0; i < info.size(); i++) {
        int a = info[i][0];
        int b = info[i][1];
        for (int j = 0; j < m; j++) {
            ndp[j] = INF;
        }
        for (int j = 0; j < m; j++) {
            if (dp[j] == INF) {
                continue;
            }
            if (dp[j] + a < ndp[j]) {
                ndp[j] = dp[j] + a;
            }
            if (j + b < m) {
                if (dp[j] < ndp[j + b]) {
                    ndp[j + b] = dp[j];
                }
            }
        }
        dp.swap(ndp);
    }

    int ans = *min_element(dp.begin(), dp.end());
    if (ans >= n) {
        return -1;
    }
    return ans;
}

ν”„λ‘œκ·Έλž˜λ¨ΈμŠ€ λ¬Έμ œλ“€μ€ 항상 μ½”λ“œ κ΅¬ν˜„ λ””ν…ŒμΌ λΆ€λΆ„μ—μ„œ μ‘°κΈˆμ”© λ§‰ν˜€μ„œ μ§€μ„ μƒλ‹˜ 도움 λ°›λŠ” 것 κ°™λ„€μš”.
문제 ν‘Έμ‹œλŠλΌ 고생 λ§ŽμœΌμ…¨μ–΄μš”!

@mj010504 mj010504 merged commit 1dc0104 into main Aug 14, 2025
@Seol-Munhyeok Seol-Munhyeok mentioned this pull request Aug 18, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants