본문 바로가기
알고리즘/DP

[java 백준] 골드 5/ 4811번 알약

by Meaning_ 2022. 5. 1.
728x90
반응형

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

 

4811번: 알약

입력은 최대 1000개의 테스트 케이스로 이루어져 있다. 각 테스트 케이스는 한 줄이며, 병에 들어있는 약의 개수 N ≤ 30 가 주어진다. 입력의 마지막 줄에는 0이 하나 주어진다.

www.acmicpc.net

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import java.io.*;
import java.util.*;
 
import static java.lang.Math.max;
 
public class Main {
    public static long cnt=0;
    public static long dp[];
    public static int n;
    public static void main(String[]args) throws IOException {
 
        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
        dp=new long[31];
        dp[0]=1;
        dp[1]=1;
        dp[2]=2;
 
        while(true){
            n=Integer.parseInt(br.readLine());
            if(n==0) {
                break;
            }
            else{
               
                for(int i=3;i<=30;i++) {
                    cnt=0;//초기화
                    for (int j = 0; j < i; j++) {
                        cnt += dp[j] * dp[i - 1 - j];
                    }
                    dp[i] = cnt;
                }
 
                System.out.println(dp[n]);
            }
        }
 
 
 
    }
 
 
 
 
 
 
 
}
 
cs

 

이 문제는 dp로 풀면 되는 간단한 문제인데 사실 정확하게 풀이가 이해가 잘 안간다.. 그래서 우선 이해한 부분만 정리해두려고 한다.

 

알약이 1개일 때는  WH

 

==> 1가지

 

알약이 2개일 때는 큰거 하나 작은거 하나 남으니까

 

큰거 -> 작은거 WWHH

작은거 -> 큰거 WHWH

 

==> 총 2가지

 

알약이 세개일때는 큰거 2개 작은거 한개가 남게 되는데

 

케이스 분류를 해보면

 

1) 작은거 -> 큰거 2개 

 

작은거를 먹고 나면 큰거 2개가 남는데 이게 dp[2]와 똑같다. 

dp[0]*dp[2] 

dp[2]는 2

 

큰거를 먹었을때는 큰알약 하나 작은 알약 두개가 남게 되는데 이때 여기서 케이스분류를 2개로 나눌 수 있다. 

 

2) 큰거 -> 작은거 -> 큰거

dp[1]*dp[1]

 

작은알약 2개 먹고 큰알약 1개 

 

3) 큰거 -> 큰거 -> 작은거 

 

dp[2]*dp[0]

큰거 1개 먹고 또 한번 큰거 1개 먹는것이 큰거 2개 먹는 dp[2]와 동일하다. 

 

 

n=4일 때

 

작은거 -> 큰거 3개

dp[0]*dp[3]

큰거 -> 작은거 -> 큰거 2개

dp[1]*dp[2]

큰거 -> 큰거 -> 작은거 -> 큰거

dp[2]*dp[1] (큰거 -> 큰거는 사실상 dp[2]와 같음)

큰거 3개 -> 작은거

dp[3]*dp[0]

 

이렇게 되면 점화식을 dp[i]=dp[j]*dp[i-1-j]라는 것을 확인할 수 있다! 

 

도움을 받은 글 

 

https://steady-coding.tistory.com/187

 

[BOJ] 백준 4811번 : 알약 (JAVA)

문제 70세 박종수 할아버지는 매일 매일 약 반알을 먹는다. 손녀 선영이는 종수 할아버지에게 약이 N개 담긴 병을 선물로 주었다. 첫째 날에 종수는 병에서 약 하나를 꺼낸다. 그 다음, 그 약을 반

steady-coding.tistory.com

 

 

풀이2 

 

이건 medicine 함수를 구현해서 재귀와 dp를 이용한건데 이게 더 직관적이여서 더 이해하기 쉬운 것 같다.

알약이 한 개 있을 땐 one-1 half+1

알약이 반개있을 땐 one은 그대로 half-1

그리고 one==0일때는 한개 짜리는 없고 반개짜리만 있을 때를 의미한다.

어차피 n이 30이하니까

medicine(30,0)까지 해서 미리 문자열의 가지수를 다 파악해서 마지막에 dp[n][0]으로만 호출해주면 된다. (처음엔 한개짜리 알약이 n개이고 반개짜리 알약은 0개니까!)

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import java.io.*;
import java.util.*;
 
import static java.lang.Math.max;
 
public class Main {
    public static long cnt=0;
    public static long dp[][];
    public static int n;
 
    //dp[한알 개수][반알 개수]
 
    public static long medicine(int one,int half){
        if (one==0){
            return 1;
        }
        if(dp[one][half]!=0){
            return dp[one][half];
        }
 
        long cnt=0;
 
        if(one!=0){
            cnt+=medicine(one-1,half+1);
        }
        if(half!=0){
            cnt+=medicine(one,half-1);
        }
 
        return dp[one][half]=cnt;
    }
 
    public static void main(String[]args) throws IOException {
 
        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
        dp=new long[31][31];
 
        dp[1][0]=1;
        dp[2][0]=2;
        dp[3][0]=5;
        medicine(30,0);
        while(true){
            n=Integer.parseInt(br.readLine());
            if(n==0){
                break;
            }else{
                System.out.println(dp[n][0]);
            }
        }
 
 
    }
 
 
 
 
 
 
 
}
cs

 

 

예를 들어 n이 3일 때

 

medicine(3,0)인데

 

1)medicine(2,1) 큰거 하나 먹으면서 큰거 두개 작은거 1개 남음

=> medicine(1,2)  또는 medicine(2,0) 

 

2-1)medicine(1,2) 큰거 한개 먹으면서 큰거 1개 작은거 2개 남음 

=>medicine(0,3) 또는 medicie(1,1)로 나눠지는데

 

3-1)medicine(0,3) 큰거 한개먹으면서 작은거 3개 남음

알약 반개 밖에 남지 않기에 1을 반환 (어차피 문자열 HHH밖에 못쓰기에 같은것이 있는 순열 하면 1)

3-2)meidicne(1,1) 작은거 1개 먹으면서 큰거 1개 작은거 1개 남음

=>medicine(0,2) 또는 medicine(1,0)

 

4-1)medicine(0,2) 큰거 1개 먹으면서 작은거 2개 남음

알약 반개 밖에 남지 않았기에 1을 반환 (어차피 문자열 HH밖에 못쓰기에 서로다른 문자열 가짓수 1)

4-2)medicine(1,0) 작은거 1개먹으면서 큰거 1개 남음

dp[1][0]과 같기에 1을 반환 

 

 

2-2)medicine(2,0) 작은거 한개 먹으면서 큰거 2개 먹음

=> dp[2][0]이 이미 있기에 2를 반환

결과적으로 dp[3][0]=5를 반환하게 된다!

728x90
반응형

댓글