✅ DFS가 최적해를 찾는 4가지 유형 구현
DFS는 최적해를 찾는 문제에서 자주 활용되며,
특히 백트래킹(Pruning)과 함께 사용하면 불필요한 탐색을 줄일 수 있음.
아래 4가지 유형을 세부 설명과 함께 C++ 코드로 정리하겠습니다.
1️⃣ N-Queens 문제 (백트래킹 활용)
📌 문제 개요
- N×N 체스판 위에 N개의 퀸을 서로 공격하지 않도록 배치하는 방법의 수를 찾는 문제
- 한 행에 하나의 퀸을 놓으면서 조건을 만족하는지 확인
📌 최적해 찾는 방식
- DFS + 백트래킹을 활용
- 퀸을 놓을 때마다 유효한 위치인지 확인(Pruning)
- 가능한 경우만 탐색하며 최적해를 찾음
📌 C++ 코드
#include <iostream>
#include <vector>
using namespace std;
int N, solutions = 0;
vector<int> board; // board[i] = j → i번째 행에 j번째 열에 퀸을 배치
// 퀸을 놓을 수 있는지 체크
bool isSafe(int row, int col) {
for (int i = 0; i < row; ++i) {
if (board[i] == col || abs(board[i] - col) == row - i) {
return false; // 같은 열 또는 대각선에 위치하면 불가능
}
}
return true;
}
// 백트래킹을 활용한 DFS 탐색
void solve(int row) {
if (row == N) { // 모든 행에 퀸을 배치 완료
solutions++;
return;
}
for (int col = 0; col < N; ++col) {
if (isSafe(row, col)) {
board[row] = col;
solve(row + 1);
}
}
}
int main() {
cout << "Enter N: ";
cin >> N;
board.resize(N);
solve(0);
cout << "Total solutions: " << solutions << endl;
return 0;
}
🏆 DFS 활용 포인트
✅ 가능한 경우만 탐색하여 불필요한 연산을 줄임
✅ 백트래킹(Pruning)으로 가지치기
✅ isSafe() 함수로 대각선, 같은 열 체크하여 탐색량 감소
✅ 최종 출력값이 놓을수 있는 경우의 총 수이기때문에 굳이 경로를 기억해 놓을 필요가 없음.
✅ 따라서, 경로 변수를 pop 하지 않고 덮어씀.
✅ 행을 하나씩 증가하면서 확인하기 때문에, 행이 겹칠일이 없음.
✅ 따라서, visited 변수를 사용하지 않음.
2️⃣ 부분집합 합 문제 (Subset Sum Problem)
📌 문제 개요
- 주어진 숫자 배열에서 합이 target과 같은 부분집합이 존재하는지 확인
- 모든 부분집합을 탐색해야 하므로 DFS 사용
- 불필요한 탐색을 줄이기 위해 백트래킹 사용
📌 최적해 찾는 방식
- DFS로 각 원소를 포함하는 경우와 포함하지 않는 경우로 분기
- 현재 합이 target을 초과하면 백트래킹(Pruning)
📌 C++ 코드
#include <iostream>
#include <vector>
using namespace std;
vector<int> nums;
int target;
bool subsetSum(int index, int currentSum) {
if (currentSum == target) return true; // 목표 합 도달 시 성공
if (index >= nums.size() || currentSum > target) return false; // 백트래킹
// 현재 원소를 포함하는 경우 OR 포함하지 않는 경우
return subsetSum(index + 1, currentSum + nums[index]) ||
subsetSum(index + 1, currentSum);
}
int main() {
int n;
cout << "Enter number of elements: ";
cin >> n;
nums.resize(n);
cout << "Enter elements: ";
for (int &num : nums) cin >> num;
cout << "Enter target sum: ";
cin >> target;
if (subsetSum(0, 0)) cout << "Subset with given sum exists!" << endl;
else cout << "No subset with given sum found." << endl;
return 0;
}
🏆 DFS 활용 포인트
✅ 모든 부분집합을 탐색해야 하므로 DFS 사용
✅ 가지치기(Pruning) → 현재 합이 target을 초과하면 즉시 종료
✅ 최종 출력값이 놓을수 있는 경우의 총 수이기때문에 굳이 경로를 기억해 놓을 필요가 없음.
✅ 따라서, 경로 변수를 pop 하지 않음.
3️⃣ 트리에서 최대 경로 합 찾기 (Maximum Path Sum in Tree)
📌 문제 개요
- 루트에서 리프까지의 경로 중 최대 합을 찾는 문제
- BFS보다 DFS가 적합 (각 경로별로 값을 유지하며 탐색 가능)
📌 최적해 찾는 방식
- DFS를 사용하여 현재 노드까지의 합을 유지하면서 탐색
- 최대 값을 계속 갱신
📌 C++ 코드
#include <iostream>
using namespace std;
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
// DFS 탐색
int maxPathSum(TreeNode* root) {
if (!root) return 0;
if (!root->left && !root->right) return root->val;
return root->val + max(maxPathSum(root->left), maxPathSum(root->right));
}
int main() {
TreeNode* root = new TreeNode(10);
root->left = new TreeNode(20);
root->right = new TreeNode(30);
root->left->left = new TreeNode(40);
root->left->right = new TreeNode(50);
cout << "Max path sum: " << maxPathSum(root) << endl;
return 0;
}
🏆 DFS 활용 포인트
✅ 각 경로별로 값을 유지하며 탐색해야 하므로 DFS 적합
✅ 재귀 호출을 통해 최대값을 지속적으로 갱신
🔥 정리
문제 유형DFS가 적합한 이유BFS가 부적합한 이유
N-Queens | 백트래킹으로 불필요한 탐색 제거 가능 | 모든 경우를 탐색하면 비효율적 |
부분집합 합 | 특정 조건을 만족하는 부분집합 탐색 가능 | 모든 경우를 한꺼번에 탐색 어려움 |
동전 조합 | 선택/미선택으로 분기 가능 | 경우의 수가 너무 많아짐 |
트리 최대 경로 합 | 현재까지의 합을 유지하며 탐색 가능 | 모든 경로를 기억해야 하므로 비효율적 |
✅ DFS는 "최단 거리"보다는 "최적 조합, 최대/최소값 찾기"에 적합! 🚀
'algorism > DFS BFS' 카테고리의 다른 글
[BFS/DFS] 여행 경로 (0) | 2025.02.12 |
---|---|
[BFS/DFS]네트워크 (0) | 2025.02.11 |
[DFS/백트래킹]DFS와 백트래킹의 차이점 (1) | 2025.01.23 |
[DFS/BFS] 단어 변환 (0) | 2024.12.19 |
[DFS/BFS]게임 맵 최단거리 (0) | 2024.12.19 |