2026.04.08
XX게임에는 피로도 시스템(0 이상의 정수로 표현합니다)이 있으며, 일정 피로도를 사용해서 던전을 탐험할 수 있습니다. 이때, 각 던전마다 탐험을 시작하기 위해 필요한 "최소 필요 피로도"와 던전 탐험을 마쳤을 때 소모되는 "소모 피로도"가 있습니다. "최소 필요 피로도"는 해당 던전을 탐험하기 위해 가지고 있어야 하는 최소한의 피로도를 나타내며, "소모 피로도"는 던전을 탐험한 후 소모되는 피로도를 나타냅니다. 예를 들어 "최소 필요 피로도"가 80, "소모 피로도"가 20인 던전을 탐험하기 위해서는 유저의 현재 남은 피로도는 80 이상 이어야 하며, 던전을 탐험한 후에는 피로도 20이 소모됩니다.
이 게임에는 하루에 한 번씩 탐험할 수 있는 던전이 여러개 있는데, 한 유저가 오늘 이 던전들을 최대한 많이 탐험하려 합니다. 유저의 현재 피로도 k와 각 던전별 "최소 필요 피로도", "소모 피로도"가 담긴 2차원 배열 dungeons 가 매개변수로 주어질 때, 유저가 탐험할수 있는 최대 던전 수를 return 하도록 solution 함수를 완성해주세요.

코드에서 사용된 던전 입장 피로도와 소모 피로도의 비율을 구해
내림차순으로 정렬하여 해결하는 알고리즘 자체가 잘못되었음
import java.util.Map;
import java.util.TreeMap;
import java.util.Collections;
class Solution {
public int solution(int k, int[][] dungeons) {
int answer = 0;
// 해시맵 내림차순으로 정렬
Map<Double, Integer> sorted = new TreeMap<>(Collections.reverseOrder());
// 필요 필요도와 소모 필요도 비율 계산하여 해시맵에 저장
for (int i = 0; i < dungeons.length; i++) {
double ratio = dungeons[i][0] / dungeons[i][1];
sorted.put(ratio, i);
}
for (int i : sorted.values()) {
if (k < dungeons[i][0]) {
break;
}
else {
k -= dungeons[i][1];
answer++;
}
}
return answer;
}
}
나눠서 비율을 계산하기보다, 필요 피로도 - 소모 피로도로 정리하였음
실패는 현저히 줄었지만, 여전히 오류가 나는 경우의 수가 있음
Hash의 특성상 같은 key값을 가지면 덮어씌워지는 문제가 발생함
import java.util.Map;
import java.util.TreeMap;
import java.util.Collections;
class Solution {
public int solution(int k, int[][] dungeons) {
int answer = 0;
// 해시맵 내림차순으로 정렬
Map<Integer, Integer> sorted = new TreeMap<>(Collections.reverseOrder());
// 필요 필요도와 소모 필요도 비율 계산하여 해시맵에 저장
for (int i = 0; i < dungeons.length; i++) {
int ratio = dungeons[i][0] - dungeons[i][1];
sorted.put(ratio, i);
}
for (int i : sorted.values()) {
if (k < dungeons[i][0]) {
continue;
}
else {
k -= dungeons[i][1];
answer++;
}
}
return answer;
}
}
알고리즘 자체가 잘못되었음.
필요 피로도와 소모 피로도로 푸는 것이 아닌, DFS로 풀어야함
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
class Solution {
public int solution(int k, int[][] dungeons) {
int answer = 0;
Map<Integer, Integer> map = new HashMap<>();
// 필요 필요도와 소모 필요도 비율 계산하여 해시맵에 저장
for (int i = 0; i < dungeons.length; i++) {
int ratio = dungeons[i][0] - dungeons[i][1];
map.put(i, ratio);
}
List<Map.Entry<Integer, Integer>> entries = new ArrayList<>(map.entrySet());
Collections.sort(entries, (a, b) -> b.getValue() - a.getValue());
for (Map.Entry<Integer, Integer> entry : entries) {
int i = entry.getKey();
if (k < dungeons[i][0]) {
continue;
}
else {
k -= dungeons[i][1];
answer++;
}
}
return answer;
}
}
지금까지 소요 시간: 58분
완전탐색(Brute-Force)
모든 경우의 수를 전부 시도해보는 방법
완전탐색 알고리즘
1
/ \
2 3
/ \ / \
4 5 6 7
탐색 순서: 1 → 2 → 3 → 4 → 5 → 6 → 7
1
/ \
2 3
/ \ / \
4 5 6 7
탐색 순서: 1 → 2 → 4 → 5 → 3 → 6 → 7
3) 백트래킹
DFS + 가지치기, 조건 있는 검색에 적합
조건에 맞지 않으면 더 이상 탐색하지 않음
불필요한 탐색을 줄여서 DFS보다 빠름
4) DFS + 백트래킹
순열/조합 문제에 적합
완전탐색이 필요한 경우
2) 최적의 순서/조합을 보장하는 기준이 없을 때
3) 모든 경우의 수를 따져야 할 때
문제를 보고 판단하는 법
입력 크기가 작을 때 완전탐색을 의심해볼 것
재귀 진행과정 정리

class Solution {
boolean[] visited;
int answer = 0;
public int solution(int k, int[][] dungeons) {
visited = new boolean[dungeons.length];
dfs(k, dungeons, 0);
return answer;
}
public void dfs(int k, int[][] dungeons, int count) {
answer = Math.max(answer, count);
for (int i = 0; i < dungeons.length; i++) {
if (!visited[i] && k >= dungeons[i][0]) { // 방문가능한 조건
visited[i] = true;
dfs(k - dungeons[i][1], dungeons, count + 1);
visited[i] = false;
}
}
}
}