JellyApple 2024. 11. 20. 23:55

1. 문제 : https://www.acmicpc.net/problem/15649

2. 문제 유형 : 백트래킹 알고리즘

3. 문제 티어 : 실버3 

4. 개념 학습

1) 백트래킹이란?
백트래킹(backtracking)은 모든 경우의 수를 탐색하면서, 조건에 맞지 않는 경우에는 중간에 탐색을 포기하고 돌아가는 방법. 즉, 안되는 길은 가지 않고, 될 가능성이 있는 길만 계속 탐색하는 알고리즘이다.

 

보통 재귀 호출을 사용하며, 탐색의 효율성을 높이기 위해 조건에 맞지 않는 경우를 빠르게 배제하는 것이 핵심이다.

 

2) 백트래킹의 동작 원리
 1. 결정 트리 탐색 :
    - 가능한 선택지를 하나씩 시도하며 트리 형태로 탐색한다.

 2. 조건 만족 여부 확인: 
    - 특정 조건에 부합하지 않으면, 더 깊이 탐색하지 않고 돌아간다.

 3. 되돌아가기(백트래킹) : 
    - 한 선택을 완료했거나 조건에 맞지 않으면, 이전 상태로 돌아가 다른 선택을 시도.

 

3) 대표적인 백트래킹 문제 유형
  - 순열
  - 조합
  - 미로 탐색
  - N-Queen 문제
  - 수열 생성 문제 

 

5. 문제 풀이

 1) 숫자를 선택하며 수열을 확장: 
   - 1부터 N까지의 숫자를 하나씩 선택하며 수열을 만든다.
   - 이미 선택된 숫자는 제외한다. => visited 등으로 방문 처리 필수! 

 2) 종료 조건 : 
   - 현재 수열의 길이가 M이 되면 수열을 출력.

 3) 백트래킹 : 
   - 재귀 호출 이후에는, 선택했던 숫자를 제거하고 방문 여부를 초기화하여 다음 숫자를 시도한다.

 

=> 핵심은 재귀 호출로 가능한 모든 수열을 탐색하는 것, 그리고 중복 체크를 위해 visited 배열, 수열 복구를 위해 current.pop() 등으로 이전 상태로 돌아가는 로직이 필요하다.

 

6. 정답 코드 

// 1. 값 입력받기
const fs = require("fs");
let input = fs.readFileSync("/dev/stdin").toString().split("\n");
let [N,M] = input[0].split(" ").map(Number);

// 2. 방문 여부와 결괏값 담을 배열
let visited = new Array(N+1).fill(false);
let arr = [];

// 3. 재귀 함수를 사용한 백트래킹 알고리즘 작성
function recursive(current, visited , n,m){
    if(current.length === m) {
        console.log(current.join(" ")); // 공백을 넣어 하나의 문자열로 반환함
        return;
     }
     
     // n 만큼 반복문 순회
     for(let i=1;i<=n;i++){
         // 만약 방문하지 않았다면
         if(!visited[i]){
             // 방문 했음을 알려주고 값을 push 해줌
             visited[i] = true;
             current.push(i);
             
             // 한 번 더 다음 단계 탐색
             recursive(current, visited, n , m);
             // 이전 단계로 돌리기 위해 current.pop() 
             current.pop();
             // 다시 방문 처리 X
             visited[i] = false;
            }
       }
  }
  
  recursive(arr, visited , n , m);

 

 

7. 시간 복잡도

N ^ N ; 중복이 가능, N이 8일때 까지만 가능하다.  => 40320번까지 가능