[백준] PS/Java

[백준 1234] 크리스마스 트리 - JAVA

SH3542 2024. 7. 13. 21:51

BOJ Link

 

 

풀이 과정

dp[n][r][g][b]을 선언 후, bottom-up dp로 진행한다.

 

n번째 레벨을 채우는 방법은 3가지이다.

 

1. 한 가지 장난감으로 꾸밀 때

n-1의 dp에서 한 장난감을 n개 빼준다.

경우의 수는 이전과 동일하므로, n-1번째 dp와 같다.

 

2. 두 가지 장난감으로 꾸밀 때

해당 경우가 가능하려면, n이 2의 배수여야 한다.

r=2, g=2 두가지 장난감으로 n=4의 레벨을 채운다면,

2개의 자리에 r을 채운 후, 나머지를 g로 채운 것과 같다. (어떤 장난감을 먼저 배치해도 상관 없다.)

r만 채운다면, g는 자동으로 올바르게 채워진다.

따라서, 이는 곧 4C2이다.

 

3. 세 가지 공으로 꾸밀 때

해당 경우가 가능하려면, n이 3의 배수여야 한다.

r=2, g=2, b=2 세가지 장난감 으로 n=6의 레벨을 채운다면,

2개의 자리에 r을 채운 후, 2개의 자리에 g를 채운 후, 나머지를 b로 채운 것과 같다.

r, g만 채운다면, b는 자동으로 올바르게 채워진다.

따라서, 이는 곧 6C4 * 4C2이다.

 

또한,

n=5와 같이 2,3의 배수가 아닌 경우는 한 가지 장난감으로 채워야 한다.

n=6과 같이 2,3의 공배수인 경우는 모든 경우의 수를 더해준다.

 

유의할 점

문제에서 조금 명확하지 않은 조건이 있다. 순서가 다른 것은 다른 경우로 간주한다.

 

헤맨 부분

내가 선언한 배열의 크기는 11*101*101*101*8byte로, 약 90MB인데도 250MB를 넘는 메모리 제한에 걸렸다.

 

런타임 메서드로 조회해보니 500MB정도를 사용하고 있었고, 해당 코드로 구현한 팩토리얼 메서드로 인한 문제였다.

 

public static long fact(int n) {
    return LongStream.rangeClosed(2, n).reduce(1, (l, r) -> l * r);
}

 

 

이후 메모리제이션 하는 코드로 바꿔서 통과했다.

 

제출 코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

class Main {

    static long fact[];

    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        int N = Integer.parseInt(st.nextToken());
        int R = Integer.parseInt(st.nextToken());
        int G = Integer.parseInt(st.nextToken());
        int B = Integer.parseInt(st.nextToken());

        long[][][][] dp = new long[N + 1][R + 1][G + 1][B + 1];
        fact = new long[N + 1];

        if (R != 0)
            dp[1][R - 1][G][B] = 1;
        if (G != 0)
            dp[1][R][G - 1][B] = 1;
        if (B != 0)
            dp[1][R][G][B - 1] = 1;

        for (int n = 2; n <= N; n++) {
            for (int r = 0; r <= R; r++) {
                for (int g = 0; g <= G; g++) {
                    for (int b = 0; b <= B; b++) {

                        // 한가지 색으로 채울 때
                        if (r - n >= 0)
                            dp[n][r - n][g][b] += dp[n - 1][r][g][b];
                        if (g - n >= 0)
                            dp[n][r][g - n][b] += dp[n - 1][r][g][b];
                        if (b - n >= 0)
                            dp[n][r][g][b - n] += dp[n - 1][r][g][b];

                        // 두가지 색으로 채울 때
                        if (n % 2 == 0) {
                            int div = n / 2;
                            long comb = comb(n, div);
                            boolean rf = r - div >= 0;
                            boolean gf = g - div >= 0;
                            boolean bf = b - div >= 0;
                            if (rf && gf)
                                dp[n][r - div][g - div][b] += dp[n - 1][r][g][b] * comb;
                            if (gf && bf)
                                dp[n][r][g - div][b - div] += dp[n - 1][r][g][b] * comb;
                            if (rf && bf)
                                dp[n][r - div][g][b - div] += dp[n - 1][r][g][b] * comb;
                        }
                        // 세가지 색으로 채울 때
                        if (n % 3 == 0) {
                            int div = n / 3;
                            if (r - div >= 0 && g - div >= 0 && b - div >= 0)
                                dp[n][r - div][g - div][b - div] += dp[n - 1][r][g][b] * comb(n, div) * comb(n - div, div);
                        }
                    }
                }
            }
        }

        long ans = 0;

        for (int r = 0; r <= R; r++) {
            for (int g = 0; g <= G; g++) {
                for (int b = 0; b <= B; b++) {
                    ans += dp[N][r][g][b];
                }
            }
        }

        System.out.println(ans);
    }

    public static long comb(int n, int r) {
        return fact(n) / (fact(n - r) * fact(r));
    }

    public static long fact(int n) {
        if (n == 1) return 1;
        return fact[n] = fact(n - 1) * n;
    }
}

'[백준] PS > Java' 카테고리의 다른 글

[백준 1106] 호텔 - JAVA  (0) 2024.07.16
[백준 1047] 울타리 - JAVA  (0) 2024.07.15
[백준 1949] 우수 마을 - JAVA  (0) 2024.07.10
[백준 1041] 주사위 - JAVA  (0) 2024.07.01
[백준 19580] 마스크가 필요해 - JAVA  (1) 2024.06.30