본문 바로가기
TIL/Java 알고리즘 문제 풀이

23.07.30

by J1-H00N 2023. 7. 30.

오늘은 백트래킹에 대해 알아보자.

 

백트래킹은 한국어로 퇴각검색이라고 하는데, 문제를 풀기위한 풀이를 이어나가다가 그 길이 답이 아닐 것 같으면 되돌아 가는 방식을 말한다. 이는 가지치기라고도 부르는데, 이 편이 이해하기 쉬울 듯 하다. 가지치기를 얼마나 잘하느냐가 효율성을 결정한다.

 

문제를 한 번 풀어보자.

 

https://www.acmicpc.net/problem/6603

 

6603번: 로또

입력은 여러 개의 테스트 케이스로 이루어져 있다. 각 테스트 케이스는 한 줄로 이루어져 있다. 첫 번째 수는 k (6 < k < 13)이고, 다음 k개 수는 집합 S에 포함되는 수이다. S의 원소는 오름차순으로

www.acmicpc.net

독일 로또는 {1, 2, ..., 49}에서 수 6개를 고른다.

로또 번호를 선택하는데 사용되는 가장 유명한 전략은 49가지 수 중 k(k>6)개의 수를 골라 집합 S를 만든 다음 그 수만 가지고 번호를 선택하는 것이다.

예를 들어, k=8, S={1,2,3,5,8,13,21,34}인 경우 이 집합 S에서 수를 고를 수 있는 경우의 수는 총 28가지이다. ([1,2,3,5,8,13], [1,2,3,5,8,21], [1,2,3,5,8,34], [1,2,3,5,13,21], ..., [3,5,8,13,21,34])

집합 S와 k가 주어졌을 때, 수를 고르는 모든 방법을 구하는 프로그램을 작성하시오.

 

이해를 위해 시간이 오래 걸렸으나 푸는데 성공하였다.

위 문제에서는 백트래킹 시점을 6번째 자리 이전에 마지막 숫자를 넣을 때로 하였다.

설명은 아래 예시와 함께 보자.

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class Main {
  static StringBuilder sb = new StringBuilder();
  static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
  static int k;
  static int[] S;
  static int[] a = new int[6];
  static boolean[] b;
  public static void main(String[] args) throws Exception {
    while (true) {
      String[] s = br.readLine().split(" ");
      k = Integer.parseInt(s[0]);
      if (k == 0) {
        return;
      }
      S = new int[k];
      b = new boolean[k];

      for (int i = 1; i <= k; i++) {
        S[i-1] = Integer.parseInt(s[i]);
      }
      solution(0, 0);
      System.out.println(sb);
      sb.setLength(0);
    }
  }

  public static void solution(int index, int start) throws Exception { // 백트래킹이 시작하는 시점은 6번째 자리 이전에 마지막 숫자를 넣었을 때
    if (index == 6) { // index는 숫자를 넣은 위치이므로 6번째 자리에 숫자를 넣으면 출력
      for (int i = 0; i < 6; i++) {
        sb.append(a[i]).append(" ");
      }
      sb.append("\n");
      return;
    }
    for (int i = start; i < k; i++) { // start는 b[] 내에서 비교를 시작할 위치
      if (b[i]) continue;
      b[i] = true;
      a[index] = S[i];
      sb.append(index+1 + " ");
      sb.append(i+1);
      sb.append("\n");
      solution(index+1, i+1);
      b[i] = false;
    }
  }
}

위 코드를 실행해보면 더 쉽게 알 수 있다.

1 1
2 2
3 3
4 4
5 5
6 6
1 2 3 4 5 6 
6 7
1 2 3 4 5 7 
5 6
6 7
1 2 3 4 6 7 
5 7
4 5
5 6
6 7
1 2 3 5 6 7 
5 7
4 6
5 7
4 7
3 4
4 5
5 6
6 7
1 2 4 5 6 7 
...

첫 번째 숫자는 숫자를 넣을 위치, 두 번째 숫자는 몇 번째 숫자를 넣을지 알려주는 역할을 하도록 설정했다.

예시에서는 편의상 각 숫자의 순서와 번호를 동일하게 했다.

6번째에 6을 넣은뒤, 1 2 3 4 5 6을 출력하고, 6은 이미 사용했으므로 그 다음에는 6번째에 7을 넣는다.

이후 반복문이 종료돼 5번째 숫자를 입력할 때로 돌아가서, 이 때 5는 이미 사용했으므로 6을 입력하고, 6번째에 7을 입력한다.

또 반복문이 종료돼 5번째 숫자를 입력할 때로 다시 돌아가서, 이 때 5와 6은 이미 사용했으므로 7을 입력한다. 여기서 7을 입력하는 순간 반복문이 종료되고, 백트래킹, 즉 가지치기가 된다. 이 반복문이 종료되면 4번째 숫자를 입력할 때로 돌아가게 되고, 이를 반복한다.

'TIL > Java 알고리즘 문제 풀이' 카테고리의 다른 글

23.07.07  (0) 2023.07.07
23.07.01  (0) 2023.07.01
23.06.29  (0) 2023.06.29
23.06.24  (0) 2023.06.24
23.06.23  (0) 2023.06.23