문제
랭퍼드 수열은 다음 조건을 만족하는 길이 2n의 수열이다.
- 1 이상 n 이하의 자연수가 각각 두 개씩 들어 있다.
- 두 개의 1 사이에는 정확히 1개의 수가 있다.
- 두 개의 2 사이에는 정확히 2개의 수가 있다.
- ...
- 두 개의 n 사이에는 정확히 n개의 수가 있다.
예를 들어 3, 1, 2, 1, 3, 2은 n=3인 랭퍼드 수열이다.
n이 주어졌을 때, 길이 2n의 랭퍼드 수열의 개수를 구하면 된다. 하지만 이렇게만 하면 재미가 없으니 조건 하나를 추가하고자 한다. x번째 수와 y번째 수는 같다는 조건이다. (이 번호는 1부터 시작한다.)
입력
세 자연수 n, x, y가 주어진다. (2 ≤ n ≤ 12, 1 ≤ x < y ≤ 2n, 1 ≤ y-x-1 ≤ n)
출력
x번째 수와 y번째 수가 같은 길이 2n의 랭퍼드 수열의 개수를 출력한다.
문제 링크 : 랭퍼든 수열쟁이야!!
설명
백트래킹 문제이다.
백트래킹 역시 만날 때마다 어려운 친구다...
해당 문제는 랭퍼드 수열이라는 수학적 이론..? 이 있는 문제다.
잠깐 랭퍼드 수열에 대해서 설명하고 가자면
어느날 스코틀랜드의 수학자 듀들리 랭퍼드(C. Dudley Langford)는 그의 아들이 컬러블록을 가지고 놀고 있는 것을 보고 있었다. 그러던 중 랭퍼드는 그의 아들이 배열한 세쌍의 컬러블록 (빨강, 파랑, 초록)이 두개의 빨간 블록은 한 블록만큼, 두개의 파란 블록은 두 블록만큼, 두개의 초록 블록은 세 블록만큼 떨어져 있는 것을 알게 되었다.
곧 그는 두개의 노란 블록을 더하여, 총 네 쌍의 컬러블록이 위와 같은 규칙을 갖도록 배열하는데 성공했다.
랭퍼드는 곧바로 이 문제의 일반화에 대해 흥미를 갖게 되었고 이 문제를 1958년에 발표하였다. 위 문제를 수학적으로 설명하면 다음과 같다:
2n개의 숫자 1,1,2,2,…,n,n을 아래의 규칙이 성립하도록 배열할 수 있을까? 임의의 1≤k≤n에 대하여, k와 또 다른 k는 서로 정확히 k만큼 떨어져 있다
출처: https://jjycjnmath.tistory.com/371
백준에 설명되어 있는 것과 이론은 거의 비슷하다.
문제는 2 * n 길이의 수열이며, 두 개의 n 사이에는 정확하게 n개의 수가 있다고 했다.
그리고 그 밑에 x번째 수와 y번째 수는 같다는 조건이 있다고 했으니 y - x - 1을 해주면 해당 숫자를 알 수 있다.
왜 y - x - 1을 해주면 그 자리의 숫자를 알 수 있냐면
예제 입력 1로 예시를 들어 2 * 3(n) = 6길이의 수열이라고 했을 때, 1 2 3 1 2 3이라고 임의의 수열을 생각해보겠다.
문제 설명에서 두 개의 n 사이에는 정확하게 n개의 수가 있다고 했다.
그 말은 즉,
두 개의 1 사이에는 정확하게 1개의 수가 있어야 한다 => 1 2 1 ...
두 개의 2 사이에는 정확하게 2개의 수가 있어야 된다고 했다. => 2 1 1 2...
이런 식으로 사이에 들어가는 숫자로 그 수를 알 수 있다.
예제 입력 1에서 x와 y는 5(y) - 1(x) 이므로 = 4가 나온다. 하지만 자기 위치를 제외하고 생각해야 하기 때문에 1을 한번 더 빼준다. ex ) 1(여기서) 2 3 4 5(여기 사이의 수)
그리고 백트래킹을 해줄 것이다.
종료 조건은 num == n * 2 + 1이 같을 때
개수를 증가시켜주고 재귀에서 빠져나온다.
visited 변수를 이용해서 사용한 숫자를 체크해줬다.
arr 변수를 이용해서 수열을 완성하였다.
코드
import java.io.*;
import java.util.*;
public class Main {
static int n, x, y;
static int[] arr;
static boolean[] visited;
static int cnt;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
n = Integer.parseInt(st.nextToken());
x = Integer.parseInt(st.nextToken());
y = Integer.parseInt(st.nextToken());
arr = new int[25]; // n의 최대 12 * 2 = 24 + 1
visited = new boolean[13]; // n의 최대 12 + 1
cnt = 0;
arr[x] = arr[y] = y - x - 1; // 양쪽으로 해당 숫자 저장
visited[y - x - 1] = true; // 사용된 숫자 표시
dfs(1); // 재귀 시작
System.out.println(cnt);
}
static void dfs(int num) {
if (num == n * 2 + 1) { // 랭퍼드 수열의 길이만큼을 arr에 채우면
cnt++; // 개수를 증가시켜준다
return;
}
if (arr[num] == 0) { // 현재 아무 숫자도 저장되어 있지 않은 상태
for (int i = 1; i <= n; i++) {
if (!visited[i]) { // 해당 숫자가 사용되지 않았다면
if (num + i + 1 <= 2 * n && arr[num + i + 1] == 0) { // num 개 뒤의 인덱스에도 저장이 안되어 있는지 확인
visited[i] = true;
arr[num] = arr[num + i + 1] = i;
dfs(num + 1); // 숫자를 증가 시킨 후 재귀
arr[num] = arr[num + i + 1] = 0; // 해당 숫자를 방문하기 전으로 돌아감
visited[i] = false; // 해당 숫자 방문하기 전으로 변경
}
}
}
}
else dfs(num + 1);
}
}
'알고리즘' 카테고리의 다른 글
[Java] 백준 27127 수 나누기 게임 (1) | 2023.12.20 |
---|---|
[Java] LeetCode 70. Climbing Stairs (0) | 2023.12.18 |
[Java] 백준 2579 계단 오르기 (2) | 2023.12.16 |
[Java] 백준 2589 보물섬 (0) | 2023.12.16 |
[Java] 백준 1012 유기농 배추 (0) | 2023.12.16 |