2775 - 부녀회장이 될테야

(https://www.acmicpc.net/problem/2775)

문제

평소 반상회에 참석하는 것을 좋아하는 주희는 이번 기회에 부녀회장이 되고 싶어

각 층의 사람들을 불러 모아 반상회를 주최하려고 한다.

 

이 아파트에 거주를 하려면 조건이 있는데,

“a층의 b호에 살려면 자신의 아래(a-1)층의 1호부터 b호까지 사람들의 수의 합만큼 사람들을 데려와 살아야 한다”

는 계약 조항을 꼭 지키고 들어와야 한다.

아파트에 비어있는 집은 없고 모든 거주민들이 이 계약 조건을 지키고 왔다고 가정했을 때,

주어지는 양의 정수 k와 n에 대해 k층에 n호에는 몇 명이 살고 있는지 출력하라.

단, 아파트에는 0층부터 있고 각층에는 1호부터 있으며, 0층의 i호에는 i명이 산다.

입력

첫 번째 줄에 Test case의 수 T가 주어진다. 그리고 각각의 케이스마다 입력으로 첫 번째 줄에 정수 k, 두 번째 줄에 정수 n이 주어진다

출력

각각의 Test case에 대해서 해당 집에 거주민 수를 출력하라.

제한

1 ≤ k, n ≤ 14


- 틀린 코드 -

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def sigma(x):
    if x == 1:
        return 1
    else:
        return sigma(x-1+ x
    
= int(input())
lst = []
 
for i in range(T):
    f = int(input())
    r = int(input())
    
    for i in range(1, r+1):
        lst.append(sigma(i))
    print(lst)
    
    for i in range(0, f):
        for j in range(len(lst)):
            lst[j] = sigma(lst[j])
    print(lst)
 
#개같이 실패
 
cs

단순히 1,2,3,4,5.. 형식의 리스트의 요소들에 그냥 시그마를 쳐버리는 꼴이기 때문에

문제에서 요구한 출력이 안 나온다.

 

- 맞힌 코드 -

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#k, n이 1이상 14이하이기 때문에, 시간초과는 딱히 신경쓰지 않아도 될 듯 하다.
 
= int(input())
 
for i in range(T):
    k = int(input())
    n = int(input())    #여기까지 변수 받기
    
    lst = [ i for i in range(1, n+1) ]  #입력받는 호수에 따른 0층 사람들의 리스트
    
    for i in range(0, k):   #3 층수만큼 반복한다.
        for j in range(1, n):   #1 호수에 따라
            lst[j] += lst[j-1]  #2 그 호수 이전까지의 합으로 설정한다.
            
    print(lst[-1]) #반복할 때 마다 그 리스트의 가장 끝 값을 출력한다.
   
cs
리스트 컴프리헨션을 한 번 사용해보고 싶었다.

머리속으로 어떤 식으로 차근차근 굴러갈까 이미징을 했을 때, 좌에서 우로 합쳐지면서

한 층 위로 올라가는 형식으로 떠올렸다. (결국에는 본인이 입력한 지점에 있는 곳에 도달)


그리고 여기서 등장한 Dynamic Programming 문제.


2839 - 설탕 배달

(https://www.acmicpc.net/problem/2839)

문제

상근이는 요즘 설탕공장에서 설탕을 배달하고 있다. 상근이는 지금 사탕가게에 설탕을 정확하게 N킬로그램을 배달해야 한다. 설탕공장에서 만드는 설탕은 봉지에 담겨져 있다. 봉지는 3킬로그램 봉지와 5킬로그램 봉지가 있다.

상근이는 귀찮기 때문에, 최대한 적은 봉지를 들고 가려고 한다. 예를 들어, 18킬로그램 설탕을 배달해야 할 때, 3킬로그램 봉지 6개를 가져가도 되지만, 5킬로그램 3개와 3킬로그램 1개를 배달하면, 더 적은 개수의 봉지를 배달할 수 있다.

상근이가 설탕을 정확하게 N킬로그램 배달해야 할 때, 봉지 몇 개를 가져가면 되는지 그 수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 N이 주어진다. (3 ≤ N ≤ 5000)

출력

상근이가 배달하는 봉지의 최소 개수를 출력한다. 만약, 정확하게 N킬로그램을 만들 수 없다면 -1을 출력한다.

상근아..

약 15분 정도 비비면서 느꼈다.

단순하게 경우의 수를 따질 문제가 아니다.

벽이 느껴졌다.

그렇게 문제 분류를 보고, 다이나믹 프로그래밍 + 그리디 알고리즘을 찾아보게 되었다.


재귀적으로 생각하기 + 불필요한 계산 줄이기
 
재귀적 '=. 귀납적
작은 문제는 해결되어있고, 큰 문제를 해결하는 법
 
이미 했던 계산은 반복하지 않는다. (메모이제이션)
밑에서 부터 계산하기(바텀-업)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#남의 코드 주석달면서 이해해보기
#https://velog.io/@hye0n/BOJPython-2839.%EC%84%A4%ED%83%95-%EB%B0%B0%EB%8B%AC
 
= int(input())    #kg을 받는다.
 
def dp(n):  #다이나믹 프로그래밍 ON
    cache[3= cache[5= 1 #3kg, 5kg의 봉지는 한 개다
    cache[1= cache[2= cache[4= -1 #1, 2, 4kg일 때는 -1이다.
    #여기까지 기본적으로 1,2,3,4,5의 케이스에 대해서 array에 입력한다.
    
    #array에 없는 cache(저장값)라면, 다음과 같이 실행한다. + 아래에서 실행했을 때 array에 없으면 반복한다.(n을 찾아서)
    if n not in cache:  
        if n % 5 == 0:
            cache[n] = dp(n - 5+ 1    #5의 배수라면, dp(n-5)의 값에 1봉지를 더한다. // 5kg 한봉지 추가
        
        elif n % 3 == 0:    #3의 배수라면, dp(n-3)의 값에 1봉지를 더한다 // 3kg 한봉지 추가
            cache[n] = dp(n - 3+ 1
        #지금 당장의 kg이 5가 필요하든 3이 필요하든 결국 array에 알맞게 저장되기 때문에 상관 없음
        
        elif dp(n - 5> 0 and dp(n - 3> 0:   #5와 3의 배수가 아닌 양수일 때,
            cache[n] = min(dp(n - 5), dp(n - 3)) + 1    #5키로 뺀거 or 3키로 뺀거 한 거 중에 횟수가 가장 작은 애에서 1봉지 더한다.
        
        else:
            cache[n] = -1   #위의 경우에 다 해당이 되지 않으면 -1
    
    return cache[n] # n kg에 따른 array의 저장값(key 값)을 리턴한다.
 
cache = {}  #dict 형식으로 저장한다 => cache[3] = 1 // {3 : 1}
 
print(dp(n))    #출력
 
#하지만 이 방식은 입력받은 n으로부터 연산할 수 있는 값을 계속 찾을 때 까지 
#top-down 방식으로 진행되므로 런타임 에러가 발생할 수 있다. (스택 오버플로우)
cs

+

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# for문을 이용한 dp (bottom-up)
#https://velog.io/@hye0n/BOJPython-2839.%EC%84%A4%ED%83%95-%EB%B0%B0%EB%8B%AC
 
import sys
 
= int(sys.stdin.readline())
 
dp = [-1* 5001    #해당되지 않는 값은 -1이기 때문에, -1을 기본으로 메모이제이션 리스트를 만든다.
#(5000kg 까지 이므로 5001)
 
dp[3= dp[5= 1   #3kg, 5kg에 해당하는 1봉지를 lst에 저장해 놓는다. (초기값 설정)
 
#1,2,3,4,5가 아닌 n까지의 케이스에서 6부터 시작하기 때문에 차근차근 쌓인다.(bottom-up)
for i in range(6, n + 1):   
    if i % 5 == 0:  #만약 5의 배수라면  (문제 쪼개기)
        dp[i] = dp[i - 5+ 1   #5kg 적었던 케이스에서 봉지 수를 1 더한다. (5kg 한봉지 추가)
    elif i % 3 == 0:
        dp[i] = dp[i - 3+ 1   #5kg 케이스와 동일
    elif dp[i - 3> 0 and dp[i - 5> 0:   #5와 3의 배수가 아니고, 이전의 -3, -5kg 케이스가 존재한다면, 
        dp[i] = min(dp[i - 3], dp[i - 5]) + 1 #그 둘 중 가장 적은 봉지가 필요한 케이스에 하나 더 얹는다.
 
print(dp[n])
cs

다이나믹 프로그래밍이 무엇인지는 어느 정도 이해하였다.

주어진 상황을 쪼개서, 어떠한 방향(top-down / bottom-up + memoization)으로 차근차근 해결할 것인지 정한다.

 

그러기 위해서는 해결해야 하는 문제가

1. 다이나믹 프로그래밍이 필요한지

2. 어떤 과정으로 나누어서 설정할 것인지

3. 그 과정에서 분수령은 어떻게 결정할 것인지

를 판단, 적용하는 능력이 중요할 것 같다.

 

어느 정도 이미지로 생각해서 구현해보려고 노력해봐야할 것 같다.

내 실력으로 해보질 않았으니.. 내일 다시 도전해봐야겠다.

 

복사했습니다!