Algorithm/BOJ

[백준 - 25330] SHOW ME THE DUNGEON - Java

ikjo 2022. 7. 10. 15:51

문제 설명

 

 

25330번: SHOW ME THE DUNGEON

올 여름 출시된 RPG 게임 "SHOW ME THE DUNGEON"은 주인공 시루가 몬스터에게 침략당한 마을을 구하는 내용의 게임이다. 배경이 되는 나라는 $0, 1, 2, \cdots, N$번의 번호가 붙어있는 $N+1$개의 마을로 이루

www.acmicpc.net

 

접근 방법

해당 문제에서는 캐릭터(시루)의 체력과 마을별 몬스터의 공격력과 주민의 수가 주어졌을 때 시루가 해방시킬 수 있는 주민들의 최대 수를 요구하고 있습니다.

 

접근 방법 1 : 순열 - 시간 초과

처음 이 문제에 접근했었던 방식은 시루가 각각의 던전들을 순서를 고려하면서 방문하는 모든 경우의 수(순열)를 구함으로써 해방시킬 수 있는 주민들의 최대 수를 구하는 것이었습니다. (다만, 시루의 체력이 소진되지 않는 선에서 탐색 진행) 다음은 이에 대한 로직입니다.

 

    private static void hunt(boolean[] visited, int hp, int damaged, int savedPeopleCount) {
        result = Math.max(result, savedPeopleCount);

        for (int i = 1; i <= n; i++) {
            if (!visited[i] && hp >= damaged + monsters[i]) {
                visited[i] = true;
                hp -= (damaged + monsters[i]);
                savedPeopleCount += people[i];
                damaged += monsters[i];

                hunt(visited, hp, damaged, savedPeopleCount);

                damaged -= monsters[i];
                hp += (damaged + monsters[i]);
                savedPeopleCount -= people[i];
                visited[i] = false;
            }
        }
    }

 

하지만 현재 문제에서 주어진 던전의 개수 N 은 최대 20으로서 이러한 방식으로 접근하면 극단적인 상황에서 20!(=2,432,902,008,176,640,000) 가지의 경우의 수를 모두 탐색해야 할 수도 있습니다. (시간 초과)

 

접근 방법 2 : 정렬과 부분 집합

시루의 체력은 k로 한정되어있으며 체력 소모도 어느 던전을 먼저 방문하냐에 따라 달라지기 때문에 순열로 접근하는 방식은 매우 직관적이긴 하나, 이 접근 방법의 문제점은 '불필요하게 너무 많은 탐색'이 발생한다는 것입니다.

 

좀 더 효율적인 탐색에 있어 중요한 포인트는 시루가 k 라는 한정된 체력 안에서 최대한 많은 주민을 해방시켜야 한다는 것입니다. 이때 체력을 가장 효율적으로 소모할 수 있는 방법은 몬스터의 공격력이 적은 순으로 방문하는 것입니다. 따라서 몬스터의 공격력을 오름차순으로 정렬한 뒤에 각각의 던전을 순차적으로 방문해보는 방식을 떠올릴 수 있습니다.

 

하지만, 몬스터의 공격력이 적다고 해서(체력을 효율적으로 소모한다고 해서) 많은 주민을 해방시키리라는 보장은 없습니다. 이러한 점을 보완코자 부분 집합 방식을 떠올릴 수 있습니다. 몬스터의 공격력이 적은 순으로 방문하되, 많은 수의 주민을 해방시키고자 탐색 시 선택과 비선택을 나누는 것입니다. 이때, 시루의 체력이 중간에 모두 소진될 경우 더이상의 탐색은 의미가 없으므로 가지치기 효과도 있어 좀 더 효율적인 탐색이 가능해집니다. 혹여라도 극단적인 상황에서 모든 던전을 탐색하더라도 N이 최대 20이기에 2^20(=1,048,576)으로 충분히 제한 시간 내에 탐색해볼만한 경우의 수에 해당됩니다.

 

전체 소스 코드

import java.util.Arrays;
import java.util.Comparator;

class Main {

    static int result = 0;
    static int[][] info;

    public static void main(String[] args) throws Exception {
        int n = read(), hp = read();
        info = new int[n][2];

        for (int i = 0; i < n; i++) {
            info[i][0] = read();
        }

        for (int i = 0; i < n; i++) {
            info[i][1] = read();
        }

        Arrays.sort(info, Comparator.comparingInt(o -> o[0]));

        hunt(0, n, hp, 0, 0);

        System.out.println(result);
    }

    private static void hunt(int depth, int n, int hp, int damaged, int savedPeopleCount) {
        if (depth == n) {
            return;
        }

        if (hp < damaged + info[depth][0]) {
            return;
        }

        hp -= (damaged + info[depth][0]);
        savedPeopleCount += info[depth][1];
        damaged += info[depth][0];

        result = Math.max(result, savedPeopleCount);

        hunt(depth + 1, n, hp, damaged, savedPeopleCount);

        damaged -= info[depth][0];
        hp += (damaged + info[depth][0]);
        savedPeopleCount -= info[depth][1];

        hunt(depth + 1, n, hp, damaged, savedPeopleCount);
    }

    private static int read() throws Exception {
        int c, n = System.in.read() - 48;
        while ((c = System.in.read()) > 32) n = 10 * n + c - 48;
        return n;
    }
}

 

참고자료

 

 

 

 

 

'Algorithm > BOJ' 카테고리의 다른 글

[백준 - 14889] 스타트와 링크 - Java  (0) 2022.07.14
[백준 - 15683] 감시 - Java  (0) 2022.07.14
[백준 - 1713] 후보자 추천 - Java  (0) 2022.06.03
[백준 - 2468] 안전 영역 - Java  (0) 2022.05.23
[백준 - 1987] 알파벳 - Java  (0) 2022.05.19