[백준 문제 풀이] 15649번 N과 M (1)

Junu Kim·2026년 1월 20일
post-thumbnail

[15649] N과 M (1)

난이도: ★★☆☆☆ • solved on: 2026-01-20


문제 요약

  • 문제 유형: 백트래킹(Backtracking), 순열
  • 요구사항: 1부터 N까지의 자연수 중에서 중복 없이 M개를 고른 모든 순열을 출력해야 한다.

사용 개념

  1. 자료구조

    • int[] : 현재 선택된 수열을 저장
    • boolean[] : 숫자 사용 여부(중복 방지) 체크
    • StringBuilder : 출력 성능 최적화
  2. 알고리즘/기법

    • 재귀 (recursion)
    • 백트래킹 (backtracking)
  3. 핵심 키워드

    • 순열 (permutation)
    • 깊이 (depth)
    • 상태 복원 (backtracking)

풀이 아이디어

1. 문제 분해

  • 길이가 M인 수열을 만든다.
  • 각 자리에 1부터 N까지의 숫자를 하나씩 시도한다.
  • 이미 사용한 숫자는 다시 사용할 수 없다.
  • 길이가 M이 되면 하나의 완성된 순열로 출력한다.

2. 재귀 함수의 목적 정의 (핵심)

sequence(n, depth)의 역할

  • depth == M
    → 길이 M의 수열이 완성되었으므로 출력하고 종료한다.
  • 그렇지 않으면
    → 1부터 N까지 순회하면서 아직 사용하지 않은 숫자를 선택한다.

3. 핵심 로직 흐름

sequence(depth):
    if depth == M:
        수열 출력
        return

    for i = 1 to N:
        if i가 아직 사용되지 않았다면:
            i를 선택
            sequence(depth + 1)
            i 선택 취소 (상태 복원)

4. 백트래킹의 본질

  • 선택: visited[i] = true, tempArr[depth] = i
  • 재귀 호출: 다음 자리로 이동
  • 복원: visited[i] = false

코드

import java.util.*;

class Main {
    public static int[] tempArr;
    public static boolean[] visited;
    public static StringBuilder sb = new StringBuilder();

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        int n = sc.nextInt();
        int r = sc.nextInt();

        tempArr = new int[r];
        visited = new boolean[n + 1];

        sequence(n, 0);
        System.out.println(sb);
    }

    public static void sequence(int n, int depth) {
        if (depth == tempArr.length) {
            for (int i = 0; i < tempArr.length; i++) {
                sb.append(tempArr[i]).append(" ");
            }
            sb.append("\n");
            return;
        }

        for (int i = 1; i <= n; i++) {
            if (!visited[i]) {
                visited[i] = true;
                tempArr[depth] = i;
                sequence(n, depth + 1);
                visited[i] = false; // 상태 복원
            }
        }
    }
}

시간·공간 복잡도

  • 시간 복잡도: O(N! / (N−M)!)
  • 공간 복잡도: O(N + M)

어려웠던 점

  • 재귀를 사용해야 한다는 것은 알았지만 재귀 함수 하나가 정확히 무엇을 책임지는지를 정의하지 못해 접근이 어려웠다.
  • “이 함수가 한 번 호출될 때 정확히 무엇을 결정하는가”를 잡지 못해 로직을 외부 설명에 의존하게 되었다.

배운 점 및 팁

  • 재귀 문제에서는 반드시 “이 함수는 어떤 단위의 문제를 해결하는가”를 먼저 문장으로 정의해야 한다.
  • 백트래킹은 선택 → 재귀 → 복원 이 3단계가 항상 세트로 움직인다.
  • 순열/조합 문제에서 depth현재까지 몇 개를 골랐는지를 의미하는 지표로 이해하면 훨씬 명확해진다.

참고 및 링크


추가 연습 문제

profile
생각이 현실이 될 수 있도록 노력하는 중입니다.

0개의 댓글