알고리즘/dp

[백준알고리즘] dp - 2225번 합분해 문제

Fenderblue 2023. 4. 3. 00:44

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

 

2225번: 합분해

첫째 줄에 답을 1,000,000,000으로 나눈 나머지를 출력한다.

www.acmicpc.net

이것도 동적계획법 문제인데 뾰족한 수가 떠오르질 않는다.

0부터 N까지의 수들 중 K개를 더하여 N이 되는 경우들을 구하는 문제인데..

머리아파서 결국 못풀고 찾아봤다

방법을 찾아보니 점화식이 나온다. 어째서 그런 식이 세워진다는 건지 이해가 안가다가

표를 보며 고민해보니 좀 이해가 간다..

표를 보면 이런 규칙이 보인다

k개의 수들을 더하여 N을 만들 수 있는 경우의 수를 알기 위해서는

k-1수들을 더하여 N-0을 만들 수 있는 경우의 수,

k-1 수들을 더하여 N-1을 만들 수 있는 경우의 수,

...

k-1 수들을 더하여 N-N(0이되겠지)을 만들 수 있는 경우의 수

이들의 합이 된다!

 

k-1개의 수들을 더하여 N-0을 만들 수 있는 경우의 수도 마찬가지로

(k-1)-1개의 수들을 더하여 (N-0)-0을 만들 수 있는 경우의 수,

(k-1)-1개의 수들을 더하여 (N-0)-1을 만들 수 있는 경우의 수,

...

(k-1)-1개의 수들을 더하여 (N-0)-N을 만들 수 있는 경우의 수

이들의 합이 된다

표를 앞에서부터 채워 나가면 뒤에 있는 해답도 구할 수 있는 식이다

이런걸 bottom-up 방식이라고 하는게 맞나요?

 

정리하자면 dp[k][n]을 구하기 위해 dp[k-1][n-n]...dp[k-1][n-0]의 값들을 더하면 된다

이때 dp[k-1][n-n]에서 dp[k-1][n-1]까지의 값의 합은

dp[k][n-1]의 값과 동일하다

그러니까 dp[k][n-1]에다가 dp[k-1][n] 이 두개 값을 알면 알고자 하는 dp[k][n]의 값을 구할 수 있음

(dp[k][n-1]는 바로 왼쪽에 있는 값이고 dp[k-1][n]는 바로 위에 있는 값이다)

 

내 설명보다는

https://computer-choco.tistory.com/545

 

[알고리즘 문제 풀이][DP] 백준 2225번 - 합분해

백준 2225번 합분해 문제입니다. (DP) - 문제 설명: https://www.acmicpc.net/problem/2225 - 문제 풀이: 본 문제는 최종 합을 만들기 위해 분해하는 문제이므로, DP 점화식이 필요합니다. DP 점화식을 도출하는

computer-choco.tistory.com

이 블로그 글을 추천합니다

 

이제 이걸 코드로 구현해보았다

코드는 그래도 간단한편..?물론 풀이요령을 찾아보지 않고 풀었다면 엄청 막막했겠지만..

package week1;

import java.io.*;
import java.util.StringTokenizer;

public class Bj2225 {   //백준 2225번 합분해 문제
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringTokenizer st = new StringTokenizer(br.readLine());
        int N = Integer.parseInt(st.nextToken());   //0~N까지의 정수 이용, 합이 N이 되도록
        int K = Integer.parseInt(st.nextToken());   //k개의 정수를 이용하여 N을 구한다

        int[][] dp = new int[K][N+1]; //KxN+1 크기의 2차원 배열을 만든다(N은 0부터 시작이므로 +1한 크기로 한다)

        for (int k = 1; k <= K; k++) {  //1~k까지(실제 배열의 인덱스는 0부터 시작이므로 k-1라고 매번 해줬다)
            for (int n = 0; n <= N; n++) {  //n은 0~n까지
                if(k == 1) dp[0][n] = 1;
                else if(n==0) dp[k-1][n] = 1;
                /*맨 첫줄은 다 1이 들어간다.k가 한개라면 N이 뭐든간에 한가지 경우밖에 없으니까
                 여기서 시작해 나머지들도 구할수있음
                 N이 0인 경우 또한 K가 뭐든간에 한가지 경우뿐이다
                 */
                else{
                    dp[k-1][n] = dp[k-1][n - 1] + dp[k - 2][n]; //왼쪽에 있는 dp값 + 위에 있는 dp값
                }

            }
        }

        bw.write(Integer.toString(dp[K-1][N] % 1000000000));    //나머지 출력
        bw.flush();
        bw.close();
    }
}

 

이렇게 했더니 단박에 틀렸습니다가 떴다. ㅡㅡ

나머지 연산을 for문 내에서 바로바로 해줘야 하나 싶어서 바꿨지만 여전히 틀렸다.

반례를 더 찾아봐도 결과 제대로 나오는것 같은데 뭐가 문제지.....?

아...

일단 for문 내에서 나머지 연산을 하도록 하는건 맞는것같다(이렇게 하면 int->long 굳이 안바꿔도 되는듯..?)

그 다음 문제는 내가 숫자를 잘못쓴거였다..ㅋㅋㅋㅋ

십억, 즉 1000000000에서 0을 하나 빼먹었었음

정신 차리자.~~

 

수정 코드

package week1;

import java.io.*;
import java.util.StringTokenizer;

public class Bj2225 {   //백준 2225번 합분해 문제
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringTokenizer st = new StringTokenizer(br.readLine());
        int N = Integer.parseInt(st.nextToken());   //0~N까지의 정수 이용, 합이 N이 되도록
        int K = Integer.parseInt(st.nextToken());   //k개의 정수를 이용하여 N을 구한다

        int[][] dp = new int[K][N+1]; //KxN+1 크기의 2차원 배열을 만든다(N은 0부터 시작이므로 +1한 크기로 한다)

        for (int k = 1; k <= K; k++) {  //1~k까지(실제 배열의 인덱스는 0부터 시작이므로 k-1라고 매번 해줬다)
            for (int n = 0; n <= N; n++) {  //n은 0~n까지
                if(k == 1) dp[0][n] = 1;
                else if(n==0) dp[k-1][n] = 1;
                /*맨 첫줄은 다 1이 들어간다.k가 한개라면 N이 뭐든간에 한가지 경우밖에 없으니까
                 여기서 시작해 나머지들도 구할수있음
                 N이 0인 경우 또한 K가 뭐든간에 한가지 경우뿐이다
                 */
                else{
                    dp[k-1][n] = dp[k-1][n - 1] + dp[k - 2][n]; //왼쪽에 있는 dp값 + 위에 있는 dp값
                    dp[k-1][n] %= 1000000000;    //미리 여기서 나머지 연산을 해준다
                }

            }
        }

        bw.write(Integer.toString(dp[K-1][N]));    //나머지 출력
        bw.flush();
        bw.close();
    }
}