본문 바로가기
공부하기!/알고리즘

[백준] 동전 9084 - JAVA

by 갈매기^0^ 2024. 8. 13.

문제

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

전략

  • 동전으로 가능한 금액의 가짓수를 구하기 때문에 처음에 떠올린것은 완전탐색, 그리디 , DP
  • 이 중 테스트 케이스가 10 , 동전의 가짓수가 20 , 금액은 10000이다.
    • 완전탐색 : 동전의 종류별로 조합을 통해 금액 확인 ⇒ 시간복잡도가 20! * 10으로 너무 크다
    • 그리디 : 값이 큰 동전을 먼저 사용하는 방식은 최적해를 보장하지 않는다.
    • 시간복잡도까지 고려해서 DP로 접근하는것이 맞다고 생각해 고민하였다.
  • dp배열을 금액에 따른 가짓수로 두었을때 2중 for문으로 동전별로 가능한 금액의 가짓수를 세는것
    • dp[j] += dp[j - coin] (단, j - coin >= 0일 때)
  • 하지만 dp배열이 1차원일 경우 5+7과 7+5처럼 중복으로 가짓수를 계산할 수 있다는것을 깨닫고
  • knapsack처럼 동전의 상태를 별도로 저장하는 2차원 배열을 선택해야한다고 생각해 문제를 풀었다.

코드

package acmicpc;

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

public class Back9084_동전 {
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StringTokenizer tokens;
    static StringBuilder output = new StringBuilder();
    static int N, T, target;
    static int[] coins;
    // coin종류가 정렬되서 나옴 -> 작은거 부터 먼저 세야 전체 가짓수를 반영할 수 있음
    static int[][] dp; // [반영된 동전들 중 최대 인덱스][금액] = 가짓수
    // (5 , 7로 60만들기 5가 12개 (5+7)이 5개) -> 따라서 동전 종류 별도로 저장해야함
    //dp 배열이 1차원일 경우 5+7과 7+5처럼 중복 계산이 나올 수 있음
    // knapsack처럼 작은 동전부터 가능한 금액에 + 다음 동전의 금액

    public static void main(String[] args) throws IOException {
        tokens = new StringTokenizer(br.readLine());
        T = Integer.parseInt(tokens.nextToken());
        for (int t = 0; t < T; t++) {
            N = Integer.parseInt(br.readLine());
            coins = new int[N + 1];
            tokens = new StringTokenizer(br.readLine());
            for (int i = 1; i <= N; i++) {
                coins[i] = Integer.parseInt(tokens.nextToken());
            }
            target = Integer.parseInt(br.readLine());
            dp = new int[N + 1][target + 1];
            for(int i = 1; i < coins.length;i++){
                dp[i][0] = 1;
            }
            // ex) 3을 1과 2로 만들때 1,1,1 과 1,2 2가지 경우의 수중
            for (int i = 1; i < dp.length; i++) {
                for (int j = 1; j <= target; j++) {
                    dp[i][j] = dp[i - 1][j]; // 이부분을 통해 1,1,1은 이미 coin[1]을 통해 이미 가짓수가 반영됨
                    if (j - coins[i] >= 0 && dp[i][j - coins[i]] > 0) {
                        dp[i][j] +=  dp[i][j - coins[i]] ; // 따라서 2가 반영되기전 가능한 가짓수 dp[i][1]를 더해주면 됨.

                    }
                }
            }
            System.out.println(dp[N][target]);
//            for (int[] arr : dp) {
//                System.out.println(Arrays.toString(arr));
//            }
        }

    }

}

유의사항

  • dp[i][0]을 1로 모두 초기화 하자
  • dp[i][j-coins[i]]는 이미 dp[i-1][j-coins[i]]의 상태값을 반영했다

'공부하기! > 알고리즘' 카테고리의 다른 글

[백준] 암호코드 2011 - JAVA  (0) 2024.08.23
[백준] 타일 채우기 2133 - JAVA  (0) 2024.08.22
DP를 정리해보자!  (0) 2024.08.10
[백준] 녹색 옷 입은 애가 젤다지 4485 - JAVA  (0) 2024.05.08
힙 & 다익스트라  (0) 2024.04.23