[ν”„λ‘œκ·Έλž˜λ¨ΈμŠ€ / Java] - μ–‘κ³Ό λŠ‘λŒ€

πŸ”— 문제 링크

μ–‘κ³Ό λŠ‘λŒ€
μ½”λ”©ν…ŒμŠ€νŠΈ μ—°μŠ΅ - μ–‘κ³Ό λŠ‘λŒ€ λ¬Έμ œμž…λ‹ˆλ‹€.
school.programmers.co.kr

πŸ’‘ 문제 νŒŒμ•…

  • 이진 트리의 각 λ…Έλ“œμ—λŠ” μ–‘(0) λ˜λŠ” λŠ‘λŒ€(1)κ°€ 있고, 루트(0번 λ…Έλ“œ)μ—μ„œ μ‹œμž‘ν•©λ‹ˆλ‹€. λ£¨νŠΈλŠ” 항상 μ–‘μž…λ‹ˆλ‹€.
  • λΆ€λͺ¨ λ…Έλ“œλ₯Ό λ°©λ¬Έν•œ μƒνƒœμ—μ„œλ§Œ μžμ‹ λ…Έλ“œλ‘œ 이동할 수 있고, μ΄λ™ν•œ λ…Έλ“œμ˜ μ–‘/λŠ‘λŒ€κ°€ 일행에 μΆ”κ°€λ©λ‹ˆλ‹€.
  • λŠ‘λŒ€μ˜ μˆ˜κ°€ μ–‘μ˜ 수 이상이 되면 양이 λͺ¨λ‘ μž‘μ•„λ¨Ήνžˆλ―€λ‘œ κ·Έ μƒνƒœλŠ” ν—ˆμš©λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.
  • λŠ‘λŒ€μ—κ²Œ μž‘μ•„λ¨Ήνžˆμ§€ μ•Šκ³  λͺ¨μ„ 수 μžˆλŠ” μ–‘μ˜ μ΅œλŒ€ 수λ₯Ό κ΅¬ν•©λ‹ˆλ‹€.
  • λ…Έλ“œλŠ” μ΅œλŒ€ 17개라 μ™„μ „ 탐색이 κ°€λŠ₯ν•©λ‹ˆλ‹€.

πŸ—οΈ μ ‘κ·Ό 방법

  • λ‹¨μˆœνžˆ μžμ‹ λ°©ν–₯으둜만 λ‚΄λ €κ°€λŠ” DFS λŠ” "μ§€κΈˆμ€ λŠ‘λŒ€κ°€ μžˆμ–΄μ„œ λͺ» κ°€μ§€λ§Œ, λ‹€λ₯Έ κ°€μ§€μ—μ„œ 양을 λ¨Όμ € λͺ¨μœΌκ³  였면 갈 수 μžˆλŠ” λ…Έλ“œ"λ₯Ό λ†“μΉ©λ‹ˆλ‹€.
  • κ·Έλž˜μ„œ ν˜„μž¬κΉŒμ§€ λ°©λ¬Έν•œ λ…Έλ“œ μ§‘ν•©μ—μ„œ λ‹€μŒμ— λ°©λ¬Έν•  수 μžˆλŠ” λͺ¨λ“  λ…Έλ“œλ₯Ό ν›„λ³΄λ‘œ μ‚ΌλŠ” λ°©μ‹μœΌλ‘œ νƒμƒ‰ν•©λ‹ˆλ‹€.
    • visited[] 둜 λ°©λ¬Έν•œ λ…Έλ“œλ₯Ό κΈ°λ‘ν•©λ‹ˆλ‹€.
    • λͺ¨λ“  κ°„μ„  [parent, child] λ₯Ό μˆœνšŒν•˜λ©΄μ„œ visited[parent] && !visited[child] 인 간선이 κ³§ λ‹€μŒμ— 갈 수 μžˆλŠ” ν›„λ³΄μž…λ‹ˆλ‹€.
      • λ…Έλ“œλ‘œ μ΄λ™ν•˜λ €λ©΄ λΆ€λͺ¨λ₯Ό 이미 λ°©λ¬Έν–ˆμ–΄μ•Ό ν•˜κ³ (visited[parent]), 아직 λ°©λ¬Έν•˜μ§€ μ•Šμ€ λ…Έλ“œμ—¬μ•Ό ν•˜κΈ°(!visited[child]) λ•Œλ¬Έμž…λ‹ˆλ‹€.
      • ν˜„μž¬ λ…Έλ“œμ˜ μžμ‹λ§Œ λ³΄λŠ” 게 μ•„λ‹ˆλΌ λ°©λ¬Έν•œ λ…Έλ“œ 전체에 λΆ™μ–΄ μžˆλŠ” λ…Έλ“œλ₯Ό λͺ¨λ‘ ν›„λ³΄λ‘œ λ³΄λ―€λ‘œ, λ‹€λ₯Έ κ°€μ§€μ—μ„œ 양을 λ¨Όμ € λͺ¨μ€ λ’€ λŒμ•„μ˜€λŠ” κ²½μš°λ„ νƒμƒ‰λ©λ‹ˆλ‹€.
  • dfs(sheep, wolf) λŠ” λ‹€μŒκ³Ό 같이 λ™μž‘ν•©λ‹ˆλ‹€.
    • wolf >= sheep 이면 양이 μž‘μ•„λ¨Ήνžˆλ―€λ‘œ 더 μ§„ν–‰ν•˜μ§€ μ•Šκ³  λ°˜ν™˜ν•©λ‹ˆλ‹€.
    • κ·Έλ ‡μ§€ μ•ŠμœΌλ©΄ answer λ₯Ό sheep 으둜 κ°±μ‹ ν•©λ‹ˆλ‹€.
    • 갈 수 μžˆλŠ” μžμ‹ λ…Έλ“œλ₯Ό λ°©λ¬Έ μ²˜λ¦¬ν•˜κ³ , 양이면 sheep + 1, λŠ‘λŒ€λ©΄ wolf + 1 둜 μž¬κ·€ ν˜ΈμΆœν•œ λ’€ λ°±νŠΈλž˜ν‚ΉμœΌλ‘œ 방문을 λ˜λŒλ¦½λ‹ˆλ‹€.
  • λ£¨νŠΈλŠ” 항상 μ–‘μ΄λ―€λ‘œ visited[0] = true 둜 두고 dfs(1, 0) μ—μ„œ μ‹œμž‘ν•©λ‹ˆλ‹€.
for (int[] edge : edges) {
    int parent = edge[0];
    int child = edge[1];

    if (visited[parent] && !visited[child]) {
        visited[child] = true;

        if (info[child] == 0) dfs(sheep + 1, wolf); // μ–‘
        else dfs(sheep, wolf + 1); // λŠ‘λŒ€

        visited[child] = false;
    }
}

⏱️ λ³΅μž‘λ„ 뢄석

μ‹œκ°„ λ³΅μž‘λ„

  • λ…Έλ“œ 수λ₯Ό n (μ΅œλŒ€ 17) 이라고 ν•˜λ©΄, ν•œ 번의 dfs ν˜ΈμΆœμ€ λͺ¨λ“  간선을 ν•œ λ²ˆμ”© ν™•μΈν•˜λ―€λ‘œ O(n) μž…λ‹ˆλ‹€.
  • 호좜 νšŸμˆ˜λŠ” κ°€λŠ₯ν•œ λ°©λ¬Έ μˆœμ„œμ˜ μˆ˜μ— λΉ„λ‘€ν•©λ‹ˆλ‹€. 이둠적인 μƒν•œμ€ n! μ΄μ§€λ§Œ, 이진 트리라 ν•œ λ²ˆμ— 갈 수 μžˆλŠ” 후보가 μ œν•œλ˜κ³  wolf >= sheep 이 λ˜λŠ” κ°€μ§€λŠ” λ°”λ‘œ μž˜λ €μ„œ μ‹€μ œ νƒμƒ‰λŸ‰μ€ 훨씬 μ μŠ΅λ‹ˆλ‹€. κ·Έλž˜μ„œ n ≀ 17 μ—μ„œ ν†΅κ³Όν•©λ‹ˆλ‹€.
  • 같은 λ°©λ¬Έ 집합이 λ‹€λ₯Έ μˆœμ„œλ‘œ μ—¬λŸ¬ 번 탐색될 수 μžˆμœΌλ―€λ‘œ, 더 μ•ˆμ „ν•˜κ²Œ ν’€λ €λ©΄ λ°©λ¬Έ 집합을 λΉ„νŠΈλ§ˆμŠ€ν¬λ‘œ μ €μž₯ν•΄ λ©”λͺ¨μ΄μ œμ΄μ…˜ν•˜λ©΄ μƒνƒœκ°€ μ΅œλŒ€ 2^n 개둜 쀄어 O(2^n Γ— n) (μ•½ 200만 번) μž…λ‹ˆλ‹€.

곡간 λ³΅μž‘λ„

  • visited λ°°μ—΄κ³Ό μž¬κ·€ κΉŠμ΄κ°€ 각각 O(n) μž…λ‹ˆλ‹€.

μ™œ 이 λ°©λ²•μœΌλ‘œ ν’€μ–΄μ•Ό ν•˜λŠ”κ°€

  • λ£¨νŠΈμ—μ„œ μžμ‹ λ°©ν–₯으둜만 λ‚΄λ €κ°€λŠ” λ‹¨μˆœ DFS
    • μ§€κΈˆμ€ λŠ‘λŒ€ λ•Œλ¬Έμ— λͺ» κ°€λŠ” λ…Έλ“œκ°€, λ‹€λ₯Έ κ°€μ§€μ—μ„œ 양을 λ¨Όμ € λͺ¨μœΌλ©΄ 갈 수 있게 λ˜λŠ” 경우λ₯Ό λ†“μΉ©λ‹ˆλ‹€.
    • λ°©λ¬Έν•œ λ…Έλ“œ 전체에 뢙은 후보λ₯Ό λͺ¨λ‘ μ‹œλ„ν•΄μ•Ό ν•˜λ―€λ‘œ λ°©λ¬Έ 집합을 μƒνƒœλ‘œ μ‚ΌλŠ” 탐색이 ν•„μš”ν•©λ‹ˆλ‹€.
  • 항상 양이 μžˆλŠ” λ…Έλ“œλ₯Ό λ¨Όμ € κ°€λŠ” 그리디
    • 양을 λ¨Όμ € λͺ¨μœΌλ‹€ 보면 λŠ‘λŒ€ 뒀에 μžˆλŠ” 양듀을 영영 μ–»μ§€ λͺ»ν•  수 μžˆμŠ΅λ‹ˆλ‹€. λ•Œλ‘œλŠ” λŠ‘λŒ€λ₯Ό 데렀가야 더 λ§Žμ€ 양을 μ–»μœΌλ―€λ‘œ κ΅­μ†Œμ μΈ μ„ νƒμœΌλ‘œλŠ” μ΅œμ ν•΄λ₯Ό 보μž₯ν•  수 μ—†μŠ΅λ‹ˆλ‹€.
  • λ…Έλ“œ μˆ˜κ°€ μž‘λ‹€λŠ” μ œμ•½ ν™œμš©
    • n ≀ 17 이라 완전탐색과 λ°±νŠΈλž˜ν‚Ήμ΄ κ°€λŠ₯ν•©λ‹ˆλ‹€. λ…Έλ“œκ°€ 수천 개라면 이 방법은 μ“Έ 수 μ—†κ³  λ‹€λ₯Έ 접근이 ν•„μš”ν•©λ‹ˆλ‹€.

🧩 μ½”λ“œ κ΅¬ν˜„

class Solution {

    int answer;
    boolean[] visited;
    int[] info;
    int[][] edges;

    public int solution(int[] info, int[][] edges) {
        this.info = info;
        this.edges = edges;
        visited = new boolean[info.length];

        visited[0] = true;

        dfs(1, 0);

        return answer;
    }

    private void dfs(int sheep, int wolf) {
        if (wolf >= sheep) return;

        answer = Math.max(answer, sheep);

        for (int[] edge : edges) {
            int parent = edge[0];
            int child = edge[1];

            if (visited[parent] && !visited[child]) {
                visited[child] = true;

                if (info[child] == 0) dfs(sheep + 1, wolf); // μ–‘
                else dfs(sheep, wolf + 1); // λŠ‘λŒ€

                visited[child] = false;
            }
        }
    }
}