Home [BaekJoon] 1010 닀리놓기 JAVA
Post
Cancel

[BaekJoon] 1010 닀리놓기 JAVA

πŸ”— λ°±μ€€ 1010 문제 https://www.acmicpc.net/problem/1010

문제

μž¬μ›μ΄λŠ” ν•œ λ„μ‹œμ˜ μ‹œμž₯이 λ˜μ—ˆλ‹€. 이 λ„μ‹œμ—λŠ” λ„μ‹œλ₯Ό 동μͺ½κ³Ό μ„œμͺ½μœΌλ‘œ λ‚˜λˆ„λŠ” 큰 일직선 λͺ¨μ–‘μ˜ 강이 흐λ₯΄κ³  μžˆλ‹€. ν•˜μ§€λ§Œ μž¬μ›μ΄λŠ” 닀리가 μ—†μ–΄μ„œ μ‹œλ―Όλ“€μ΄ 강을 κ±΄λ„ˆλŠ”λ° 큰 λΆˆνŽΈμ„ κ²ͺκ³  μžˆμŒμ„ μ•Œκ³  닀리λ₯Ό μ§“κΈ°λ‘œ κ²°μ‹¬ν•˜μ˜€λ‹€. κ°• μ£Όλ³€μ—μ„œ 닀리λ₯Ό 짓기에 μ ν•©ν•œ 곳을 μ‚¬μ΄νŠΈλΌκ³  ν•œλ‹€. μž¬μ›μ΄λŠ” κ°• 주변을 λ©΄λ°€νžˆ 쑰사해 λ³Έ κ²°κ³Ό κ°•μ˜ μ„œμͺ½μ—λŠ” N개의 μ‚¬μ΄νŠΈκ°€ 있고 동μͺ½μ—λŠ” M개의 μ‚¬μ΄νŠΈκ°€ μžˆλ‹€λŠ” 것을 μ•Œμ•˜λ‹€. (N ≀ M)

μž¬μ›μ΄λŠ” μ„œμͺ½μ˜ μ‚¬μ΄νŠΈμ™€ 동μͺ½μ˜ μ‚¬μ΄νŠΈλ₯Ό λ‹€λ¦¬λ‘œ μ—°κ²°ν•˜λ €κ³  ν•œλ‹€. (μ΄λ•Œ ν•œ μ‚¬μ΄νŠΈμ—λŠ” μ΅œλŒ€ ν•œ 개의 λ‹€λ¦¬λ§Œ 연결될 수 μžˆλ‹€.) μž¬μ›μ΄λŠ” 닀리λ₯Ό μ΅œλŒ€ν•œ 많이 μ§€μœΌλ €κ³  ν•˜κΈ° λ•Œλ¬Έμ— μ„œμͺ½μ˜ μ‚¬μ΄νŠΈ 개수만큼 (N개) 닀리λ₯Ό μ§€μœΌλ €κ³  ν•œλ‹€. λ‹€λ¦¬λΌλ¦¬λŠ” μ„œλ‘œ 겹쳐질 수 μ—†λ‹€κ³  ν•  λ•Œ 닀리λ₯Ό 지을 수 μžˆλŠ” 경우의 수λ₯Ό κ΅¬ν•˜λŠ” ν”„λ‘œκ·Έλž¨μ„ μž‘μ„±ν•˜λΌ.

img


μž…λ ₯

μž…λ ₯κ³Ό 첫 μ€„μ—λŠ” ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€μ˜ 개수 Tκ°€ 주어진닀. κ·Έ λ‹€μŒ 쀄뢀터 각각의 ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€μ— λŒ€ν•΄ κ°•μ˜ μ„œμͺ½κ³Ό 동μͺ½μ— μžˆλŠ” μ‚¬μ΄νŠΈμ˜ 개수 μ •μˆ˜ N, M (0 < N <= M < 30) 이 주어진닀.


좜λ ₯

각 ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€μ— λŒ€ν•΄ 주어진 쑰건 ν•˜μ— 닀리λ₯Ό 지을 수 μžˆλŠ” 경우의 수λ₯Ό 좜λ ₯ν•œλ‹€.


풀이

일단 이 문제λ₯Ό λ”± λ΄€μ„λ•Œ λ“  경우의 μˆ˜μ™€ 쀑볡이 ν—ˆλ½λ˜μ§€ μ•ŠλŠ”λ‹€λŠ” λΆ€λΆ„μ—μ„œ 쑰합을 λ– μ˜¬λ Έλ‹€.

μ‘°ν•©

n 개의 μ›μ†Œ 쀑 μˆœμ„œλ₯Ό κ³ λ €ν•˜μ§€ μ•ŠλŠ” 경우의 수. 즉, 쀑볡을 μ œκ±°ν•œ μˆœμ—΄μ΄λΌκ³  λ³Ό 수 μžˆλ‹€.

nCr = nβˆ’β‚Crβˆ’β‚ + nβˆ’β‚Cr

쑰합은 μœ„μ™€ 같은 곡식을 κ°–κ³  μžˆλŠ”λ° μ΄λŠ”,

ν•˜λ‚˜μ˜ μ›μ†Œλ₯Ό 선택할 경우 + ν•˜λ‚˜μ˜ μ›μ†Œλ₯Ό μ„ νƒν•˜μ§€ μ•Šμ„ 경우둜 λ‚˜νƒ€λ‚Ό 수 μžˆλ‹€.

예λ₯Ό λ“€λ©΄ [1, 2, 3, 4, 5] κ°€ μžˆμ„ λ•Œ 3개λ₯Ό λ½‘λŠ” 경우λ₯Ό κ΅¬ν•œλ‹€κ³  ν•˜λ©΄

  • 5λ₯Ό 미리 선택할 경우 : 남은 수 [1, 2, 3, 4]. μ„ νƒλœ 수 [5]
    • 남은 수 쀑 2개λ₯Ό 선택할 수 μžˆλŠ” 경우 = [1, 2, 5], [1, 3, 5], [1, 4, 5], [2, 3, 5], [2, 4, 5], [3, 4, 5]. 총 6개
  • 5λ₯Ό μ„ νƒν•˜μ§€ μ•Šμ„ 경우 : 남은 수 [1, 2, 3, 4]
    • 남은 수 쀑 3개λ₯Ό 선택할 수 μžˆλŠ” 경우 = [1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]. 총 4개

즉, 5Cr = 4C2 + 4C3 = 10κ°œκ°€ λœλ‹€.

뽑아야 ν•  개수인 r이 0이 되면 μ„ νƒμ˜ 여지가 μ—†μœΌλ―€λ‘œ 1을 λ¦¬ν„΄μ‹œν‚¨λ‹€.

전체 κ°œμˆ˜μ™€ 뽑아야 ν•  κ°œμˆ˜κ°€ κ°™λ‹€λ©΄ 이 λ˜ν•œ μ„ νƒμ˜ 여지가 μ—†μœΌλ―€λ‘œ 1을 λ¦¬ν„΄μ‹œν‚¨λ‹€.


그리고 0.5 μ΄ˆλΌλŠ” μ‹œκ°„ μ œν•œμ΄ μžˆκΈ°μ— Dynamic Programming μ•Œκ³ λ¦¬μ¦˜ κΈ°λ²•μ˜ Top-down 을 ν™œμš©ν•˜μ—¬ μˆ˜ν–‰ μ‹œκ°„ νš¨μœ¨μ„±μ„ ν–₯μƒμ‹œμΌœμ£Όμ—ˆλ‹€.

동적 κ³„νšλ²• Dynamic Programming

DP μ•Œκ³ λ¦¬μ¦˜ 기법은 이미 κ³„μ‚°λœ κ²°κ³Όλ₯Ό λ³„λ„μ˜ λ©”λͺ¨λ¦¬ μ˜μ—­μ— μ €μž₯ν•˜μ—¬ λ‹€μ‹œ κ³„μ‚°ν•˜μ§€ μ•Šλ„λ‘ μ„€κ³„ν•˜μ—¬ μˆ˜ν–‰ μ‹œκ°„ νš¨μœ¨μ„±μ„ ν–₯μƒμ‹œν‚€λŠ” 방법이닀.

Top-down ν•˜ν–₯식

μƒμœ„ 문제 해결을 μœ„ν•΄ ν•˜μœ„ 문제λ₯Ό μž¬κ·€λ‘œ ν˜ΈμΆœν•˜μ—¬ ν•˜μœ„ λ¬Έμ œλΆ€ν„° μƒμœ„ λ¬Έμ œκΉŒμ§€ 순차적으둜 ν•΄κ²°ν•˜λŠ” 방식. 해결해놓은 ν•˜μœ„ 문제λ₯Ό μ €μž₯ν•˜κΈ° μœ„ν•΄ Memoization이 μ‚¬μš©λœλ‹€.

Memoization

ν•œλ²ˆ κ³„μ‚°ν•œ κ²°κ³Όλ₯Ό λ©”λͺ¨λ¦¬ 곡간에 λ©”λͺ¨ν•˜λŠ” 기법.

λ©”λͺ¨μ΄μ œμ΄μ…˜ μ‚¬μš©μ„ 톡해 λͺ¨λ“  λ¬Έμ œκ°€ 단 ν•œλ²ˆμ”©λ§Œ κ³„μ‚°λœλ‹€κ³  보μž₯λ˜κΈ°μ— ν•¨μˆ˜ 호좜 νšŸμˆ˜κ°€ κ°μ†Œλœλ‹€.

λ‚΄κ°€ ν‘Ό μ½”λ“œ

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
import java.io.*;
import java.util.StringTokenizer;

public class Main {
    private static final int[][] dp = new int[30][30]; // λ©”λͺ¨μ΄μ œμ΄μ…˜ μœ„ν•œ ν–‰λ ¬, κ΅¬ν•œ 값을 λ°”λ‘œ μ €μž₯ν•΄λ‘”λ‹€

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

        int T = Integer.parseInt(br.readLine());

        for (int i = 0; i < T; i++) {
            StringTokenizer st = new StringTokenizer(br.readLine(), " ");
            int N = Integer.parseInt(st.nextToken());
            int M = Integer.parseInt(st.nextToken());

            bw.write(recursive(M, N) + "\n");
        }
        bw.flush();
        bw.close();
    }

    // M μ—μ„œ N을 λ½‘λŠ” 경우의 수
    private static int recursive(int n, int r) {

        /**
         * λ©”λͺ¨μ΄μ œμ΄μ…˜ O(n)
         * ν•œλ²ˆ κ΅¬ν•œ 값은 μ €μž₯된 κ°’ 리턴
         */
        if (dp[n][r] > 0) {
            return dp[n][r];
        }
        else if (n == r || r == 0) {
            return dp[n][r] = 1;
        }
        else {
            /**
             * Combination μ‘°ν•©
             * nCr = n-1Cr-1 + n-1Cr
             * (n-1Cr-1) : ν•˜λ‚˜μ˜ μ›μ†Œλ₯Ό 선택할 경우
             * (n-1Cr) : ν•˜λ‚˜μ˜ μ›μ†Œλ₯Ό μ„ νƒν•˜μ§€ μ•Šμ„ 경우
             */
            return dp[n][r] = recursive(n - 1, r - 1) + recursive(n - 1, r);
        }
    }
}

μ°Έκ³  λΈ”λ‘œκ·Έ https://woongsios.tistory.com/179 https://loosie.tistory.com/150

This post is licensed under CC BY 4.0 by the author.

[Java] Scanner VS BufferedReader

[BaekJoon] 1542 μ„Έμ€€μ„ΈλΉ„ JAVA