-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
148 changed files
with
608 additions
and
3,637 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
// https://school.programmers.co.kr/learn/courses/30/lessons/87946?language=javascript | ||
|
||
function solution(k, dungeons) { | ||
// k는 유저의 현재 피로도 | ||
// dungeons는 [최소 필요 피로도, 소모피로도] 가 담긴 2차원 배열 | ||
|
||
// 방문한 던전을 체크하기 위한 배열 | ||
const visited = Array(dungeons.length).fill(false); | ||
|
||
// 최대 던전 탐험 횟수 | ||
let maxCount = 0; | ||
|
||
// DFS로 모든 가능한 던전 순서를 탐색 | ||
function dfs(fatigue, count) { | ||
// 현재까지의 탐험 횟수와 최대 탐험 횟수 비교하여 갱신 | ||
maxCount = Math.max(maxCount, count); | ||
|
||
// 모든 던전을 확인 | ||
for (let i = 0; i < dungeons.length; i++) { | ||
const [최소필요피로도, 소모피로도] = dungeons[i]; | ||
|
||
// 아직 방문하지 않았고, 현재 피로도로 던전 탐험이 가능한 경우 | ||
if (!visited[i] && fatigue >= 최소필요피로도) { | ||
visited[i] = true; // 방문 표시 | ||
|
||
// 던전 탐험 후 피로도 감소시키고 다음 던전으로 | ||
dfs(fatigue - 소모피로도, count + 1); | ||
|
||
visited[i] = false; // 방문 취소 (백트래킹) | ||
} | ||
} | ||
} | ||
|
||
// 초기 피로도와 탐험 횟수 0으로 DFS 시작 | ||
dfs(k, 0); | ||
|
||
return maxCount; | ||
} | ||
|
||
// 테스트 함수 | ||
function testSolution() { | ||
const testCases = [ | ||
{ | ||
k: 80, | ||
dungeons: [ | ||
[80, 20], | ||
[50, 40], | ||
[30, 10], | ||
], | ||
expected: 3, | ||
description: '기본 예시 - 문제에서 제공된 테스트 케이스', | ||
}, | ||
{ | ||
k: 20, | ||
dungeons: [ | ||
[80, 20], | ||
[50, 40], | ||
[30, 10], | ||
], | ||
expected: 0, | ||
description: '모든 던전을 돌 수 없는 경우', | ||
}, | ||
{ | ||
k: 60, | ||
dungeons: [ | ||
[80, 20], | ||
[50, 40], | ||
[30, 10], | ||
], | ||
expected: 2, | ||
description: '일부 던전만 돌 수 있는 경우', | ||
}, | ||
{ | ||
k: 50, | ||
dungeons: [ | ||
[30, 10], | ||
[30, 10], | ||
[30, 10], | ||
], | ||
expected: 3, | ||
description: '모든 던전의 최소 필요 피로도가 같은 경우', | ||
}, | ||
{ | ||
k: 50, | ||
dungeons: [[30, 10]], | ||
expected: 1, | ||
description: '던전이 하나인 경우', | ||
}, | ||
{ | ||
k: 100, | ||
dungeons: [ | ||
[100, 30], | ||
[90, 20], | ||
[80, 40], | ||
[70, 10], | ||
[60, 20], | ||
[50, 5], | ||
], | ||
expected: 4, | ||
description: '더 많은 던전이 있는 복잡한 경우', | ||
}, | ||
]; | ||
|
||
for (let i = 0; i < testCases.length; i++) { | ||
const { k, dungeons, expected, description } = testCases[i]; | ||
const result = solution(k, dungeons); | ||
const pass = result === expected; | ||
|
||
console.log(`테스트 #${i + 1}: ${description}`); | ||
console.log(` 입력: k=${k}, dungeons=${JSON.stringify(dungeons)}`); | ||
console.log(` 기대 결과: ${expected}, 실제 결과: ${result}`); | ||
console.log(` 테스트 ${pass ? '통과 ✓' : '실패 ✗'}`); | ||
console.log('------------------------'); | ||
} | ||
} | ||
|
||
// 테스트 실행 | ||
testSolution(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
# 49번같은 그래프 문제 스스로 풀기 위한 학습 방법 | ||
|
||
이해는 했지만 스스로 풀기 어려운 상황은 알고리즘 학습 과정에서 매우 흔한 일이다. | ||
이 DFS와 백트래킹을 활용한 문제를 스스로 풀 수 있도록 단계적인 접근법을 알아보자. | ||
|
||
## 1. 문제 유형 파악하기 | ||
|
||
이 피로도 문제는 **순열(Permutation)** 문제의 응용입니다. 던전을 방문하는 모든 가능한 순서를 탐색하여 최적의 결과를 찾는 문제죠. 이런 유형의 문제는 DFS와 백트래킹 기법으로 해결합니다. | ||
|
||
## 2. 사고 과정 단계별로 익히기 | ||
|
||
문제를 풀 때 다음과 같은 단계로 접근해보세요: | ||
|
||
1. **문제 분석**: | ||
|
||
- 입력: 현재 피로도(k)와 던전 정보(dungeons) | ||
- 출력: 최대로 탐험할 수 있는 던전 수 | ||
- 제약: 던전마다 최소 필요 피로도와, 소모 피로도가 있음 | ||
|
||
2. **접근법 선택**: | ||
|
||
- 던전의 개수가 최대 8개로 적기 때문에 모든 가능한 순서를 탐색해도 됨 | ||
- 모든 순서를 시도해봐야 하므로 완전 탐색(DFS/백트래킹) 활용 | ||
|
||
3. **알고리즘 구조화**: | ||
- 방문 여부를 체크할 배열 필요 | ||
- 현재까지의 최대 던전 수를 기록할 변수 필요 | ||
- DFS 함수 내에서 모든 던전을 순회하며 방문 가능한 던전을 방문 | ||
|
||
## 3. 연습을 위한 단계별 접근 | ||
|
||
### 단계 1: 기본 개념 다지기 | ||
|
||
먼저 DFS와 백트래킹의 기본 개념을 확실히 이해하세요. | ||
|
||
``` | ||
DFS 기본 구조: | ||
1. 현재 상태 체크 | ||
2. 가능한 모든 선택지에 대해: | ||
- 선택 진행 | ||
- 다음 단계 재귀 호출 | ||
- 선택 취소(백트래킹) | ||
``` | ||
|
||
### 단계 2: 쉬운 순열 문제부터 풀어보기 | ||
|
||
배열 [1,2,3]의 모든 순열을 구하는 간단한 문제로 시작해보세요. | ||
|
||
```javascript | ||
function permutation(arr) { | ||
const result = []; | ||
const visited = Array(arr.length).fill(false); | ||
const current = []; | ||
|
||
function dfs() { | ||
if (current.length === arr.length) { | ||
result.push([...current]); | ||
return; | ||
} | ||
|
||
for (let i = 0; i < arr.length; i++) { | ||
if (!visited[i]) { | ||
visited[i] = true; | ||
current.push(arr[i]); | ||
dfs(); | ||
current.pop(); | ||
visited[i] = false; | ||
} | ||
} | ||
} | ||
|
||
dfs(); | ||
return result; | ||
} | ||
``` | ||
|
||
<details> | ||
<summary>current.pop 설명 보기</summary> | ||
|
||
# 순열 백트래킹에서 current.pop()의 역할 설명 | ||
|
||
`current.pop()`은 백트래킹(backtracking) 알고리즘의 핵심 부분입니다. 이 코드가 하는 일과 그 과정을 단계별로 설명해 드리겠습니다. | ||
|
||
## current.pop()의 역할 | ||
|
||
순열 알고리즘에서 `current.pop()`은 **이전 상태로 돌아가기 위해** 사용됩니다. 모든 가능한 경로를 탐색한 후, 다른 경로를 시도하기 위해 이전 상태로 되돌리는 역할을 합니다. | ||
|
||
## 작동 과정 예시 | ||
|
||
[1, 2, 3] 배열의 순열을 찾는 과정을 통해 설명해 보겠습니다: | ||
|
||
### 1단계: 첫 번째 요소 선택 | ||
|
||
- `current = []`, `visited = [false, false, false]` | ||
- 요소 1 선택: `current = [1]`, `visited = [true, false, false]` | ||
- `dfs()` 재귀 호출 | ||
|
||
### 2단계: 두 번째 요소 선택 | ||
|
||
- 요소 2 선택: `current = [1, 2]`, `visited = [true, true, false]` | ||
- `dfs()` 재귀 호출 | ||
|
||
### 3단계: 세 번째 요소 선택 | ||
|
||
- 요소 3 선택: `current = [1, 2, 3]`, `visited = [true, true, true]` | ||
- 조건 충족: `current.length === arr.length` | ||
- 결과에 `[1, 2, 3]` 추가 | ||
- 이제 백트래킹 시작! | ||
|
||
### 4단계: 첫 번째 백트래킹 (`current.pop()` 실행) | ||
|
||
```javascript | ||
current.pop(); // 3을 제거 | ||
visited[2] = false; // 3을 미방문 상태로 변경 | ||
``` | ||
|
||
- `current = [1, 2]`, `visited = [true, true, false]` | ||
- 이 재귀 레벨의 for 루프가 종료됨 | ||
- 이전 재귀 호출로 돌아감 | ||
|
||
### 5단계: 두 번째 백트래킹 (`current.pop()` 실행) | ||
|
||
```javascript | ||
current.pop(); // 2를 제거 | ||
visited[1] = false; // 2를 미방문 상태로 변경 | ||
``` | ||
|
||
- `current = [1]`, `visited = [true, false, false]` | ||
- 다음 요소(3)로 진행 | ||
|
||
### 6단계: 새로운 경로 탐색 | ||
|
||
- 요소 3 선택: `current = [1, 3]`, `visited = [true, false, true]` | ||
- `dfs()` 재귀 호출 | ||
|
||
### 7단계: 남은 요소 선택 | ||
|
||
- 요소 2 선택: `current = [1, 3, 2]`, `visited = [true, true, true]` | ||
- 결과에 `[1, 3, 2]` 추가 | ||
- 다시 백트래킹... | ||
|
||
## 시각적 이해 | ||
|
||
``` | ||
[] | ||
/ | ||
[1] | ||
/ \ | ||
[1,2] [1,3] | ||
| | | ||
[1,2,3] [1,3,2] | ||
pop() pop() | ||
↓ ↓ | ||
[1,2] [1,3] | ||
pop() pop() | ||
↓ ↓ | ||
[1] [1] | ||
↓ | ||
다른 경로 탐색... | ||
``` | ||
|
||
## 정리 | ||
|
||
1. `current.pop()`는 현재 탐색 중인 순열에서 마지막 요소를 제거합니다. | ||
2. 이렇게 함으로써 이전 상태로 돌아가서 다른 조합을 시도할 수 있습니다. | ||
3. `visited[i] = false`와 함께 사용되어 방문 표시도 초기화합니다. | ||
4. 이 과정이 없다면 모든 가능한 순열을 생성할 수 없습니다. | ||
|
||
이 백트래킹 과정은 "**시도해보고 -> 안되면 되돌아가서 -> 다른 방법 시도**"라는 전략의 구현입니다. 마치 미로에서 길을 찾다가 막다른 길에 도달하면 되돌아가서 다른 경로를 시도하는 것과 같습니다. | ||
|
||
--- | ||
|
||
</details> | ||
|
||
### 단계 3: 문제의 단순 버전부터 풀기 | ||
|
||
피로도 문제를 단순화해서 먼저 풀어보세요. 예를 들어 던전이 2개만 있는 경우부터 시작하여 점점 복잡하게 만들어 보세요. | ||
|
||
### 단계 4: 문제를 스스로 다시 풀기 | ||
|
||
이 문제를 전체 코드를 보지 않고 처음부터 풀어보세요: | ||
|
||
1. 함수 틀부터 만들기 | ||
2. 필요한 변수(visited, maxCount) 정의 | ||
3. DFS 함수의 매개변수(fatigue, count) 정의 | ||
4. 중단 조건과 최댓값 갱신 로직 작성 | ||
5. 각 던전을 방문하는 로직 작성 | ||
6. 백트래킹 구현 | ||
|
||
## 4. 문제 풀이 연습 전략 | ||
|
||
1. **패턴 연습**: 유사한 DFS/백트래킹 문제를 여러 개 풀어보세요. | ||
2. **템플릿 만들기**: 자주 사용하는 DFS/백트래킹 패턴을 템플릿화하세요. | ||
3. **시각화**: 재귀 호출의 진행 과정을 그림으로 그려보세요. | ||
4. **문제 변형**: 기존 문제를 살짝 변형해서 스스로 풀어보세요. | ||
5. **설명하기**: 문제 풀이 과정을 다른 사람에게 설명하듯 글로 작성해보세요. | ||
|
||
## 5. 추천 연습 문제 | ||
|
||
이 문제와 유사한 DFS/백트래킹 문제들입니다: | ||
|
||
- 프로그래머스: N-Queen, 단어 변환 | ||
- 백준: 연산자 끼워넣기(14888), 스타트와 링크(14889) | ||
|
||
꾸준히 연습하면 이런 유형의 문제를 스스로 풀 수 있는 능력이 생깁니다. 화이팅하세요! 😊 |
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
// 49. 피로도 | ||
|
||
//백트래킹을 위한 dfs 함수 생성 | ||
function dfs(curk, count, dungeons, visited) { | ||
let answerMax = count; | ||
for (let i = 0; i < dungeons.length; i++) { | ||
// 현재 피로도가 i번째 던전의 최소 필요 피로도보다 크거나 같고, | ||
// i번째 던전을 방문한 적이 없다면 | ||
if (curk >= dungeons[i][0] && visited[i] === 0) { | ||
visited[i] = 1; | ||
// 현재까지의 최대 탐험 가능 던전 수와 | ||
// i번째 던전에서 이동할 수 있는 최대 탐험 가능 던전 수 중 큰 값을 선택하여 업데이트 | ||
// 재귀 호출하면서 다른 모든 경우의 수도 확인 | ||
answerMax = Math.max(answerMax, dfs(curk - dungeons[i][1], count + 1, dungeons, visited)); | ||
// 백트래킹을 위해 방문 표시를 해제 | ||
// 다음 경로 탐색을 위해 현재 던전의 방문 여부를 초기화해야 함 | ||
visited[i] = 0; | ||
} | ||
} | ||
return answerMax; | ||
} | ||
|
||
function solution(k, dungeons) { | ||
const visited = new Array(dungeons.length).fill(0); | ||
const answerMax = dfs(k, 0, dungeons, visited); | ||
return answerMax; | ||
} | ||
|
||
|
||
// // ----- | ||
// // 1️⃣ 첫 번째 호출 | ||
// dfs(80, 0, dungeons, [0,0,0]) | ||
// // i = 0 일 때 (첫 번째 던전 선택) | ||
// // 조건: 80 >= 80 && visited[0] === 0 -> true | ||
// visited = [1,0,0] | ||
// ↓ | ||
// // 2️⃣ 두 번째 호출 | ||
// dfs(60, 1, dungeons, [1,0,0]) | ||
// // i = 1 일 때 (두 번째 던전 선택) | ||
// // 조건: 60 >= 50 && visited[1] === 0 -> true | ||
// visited = [1,1,0] | ||
// ↓ | ||
// // 3️⃣ 세 번째 호출 | ||
// dfs(20, 2, dungeons, [1,1,0]) | ||
// // i = 2 일 때 (세 번째 던전 선택) | ||
// // 조건: 20 < 30 -> false | ||
// // 더 이상 진행 불가, count = 2 반환 | ||
// ↓ | ||
// visited = [1,0,0] (백트래킹) | ||
// // i = 2 일 때 (세 번째 던전 선택) | ||
// // 조건: 60 >= 30 && visited[2] === 0 -> true | ||
// visited = [1,0,1] | ||
// ↓ | ||
// // 4️⃣ 네 번째 호출 | ||
// dfs(50, 2, dungeons, [1,0,1]) | ||
// // i = 1 일 때 (두 번째 던전 선택) | ||
// // 조건: 50 >= 50 && visited[1] === 0 -> true | ||
// visited = [1,1,1] | ||
// ↓ | ||
// // 5️⃣ 다섯 번째 호출 | ||
// dfs(10, 3, dungeons, [1,1,1]) | ||
// // 모든 던전 방문 완료, count = 3 반환 | ||
// // 이것이 최대값이 됨! | ||
|
||
// // 이후 다른 순서들도 시도하지만 3보다 큰 값은 나오지 않음 |
Oops, something went wrong.