알고리즘
백트래킹
코딩 화이팅
2023. 3. 23. 13:39
백트래킹 기법
- 어떤 노드의 유망성을 점검한 후에 유망(promising)하지 않다고 결정되면 그 노드의 부모로 되돌아가(backtracking) 다음 자식 노드로 감.
- 어떤 노드를 방문하였을 때 그 노드를 포함한 경로가 해답이 될 수 없으면 그 노드는 유망하지 않다고 하며, 반대로 해답의 가능성이 있으면 유망하다고 한다.
- 가지치기(pruning) : 유망하지 않는 노드가 포함되는 경로는 더 이상 고려하지 않는다.
백트래킹을 이용한 알고리즘
- 상태 공간 트리의 깊이 우선 검색을 실시한다.
- 각 노드가 유망한지를 점검한다.
- 만일 그 노드가 유망하지 않으면, 그 노드의 부모 노드로 돌아가서 검색을 계속한다.
단순 순열
package day0323_백트래킹;
//반복문
public class 순열_1 {
static int[] nums;
static int N;
public static void main(String[] args) {
nums = new int[] {0,1,2};
N = nums.length;
for(int i = 0 ; i<N; i++) {
for(int j = 0 ; j<N; j++) {
if(i != j) {
for(int k = 0 ; k<N; k++) {
if(i != k && j != k) {
System.out.printf("%d %d %d\n", nums[i], nums[j], nums[k]);
}
}
}
}
}
}
}
//
0 1 2
0 2 1
1 0 2
1 2 0
2 0 1
2 1 0
swap을 통한 순열 생성
package day0323_백트래킹;
import java.util.Arrays;
//swap
public class 순열_2 {
static int[] nums; //배열
static int N; //원소수
public static void main(String[] args) {
nums = new int[] {0,1,2};
N = nums.length;
perm(0);
}
//idx : 현재 판단 하는 위치
public static void perm(int idx) {
//기저조건
if(idx == N) {
System.out.println(Arrays.toString(nums));
return;
}
//재귀조건
for(int i = idx; i < N; i++) {
swap(i, idx);
perm(idx+1);
swap(i, idx); //원상복구 하는 과정이 필요함. why?
}
}
//swap 메서드
//바꿀 인덱스 2개가 인자로 넘어와야한다. (배열을 static 하게 만들었으니까)
//위의 경우가 아니라면 배열까지 인자로 같이 넘겨줘야한다.
public static void swap(int a, int b) {
int tmp = nums[a];
nums[a] = nums[b];
nums[b] = tmp;
}
}
//
[0, 1, 2]
[0, 2, 1]
[1, 0, 2]
[1, 2, 0]
[2, 1, 0]
[2, 0, 1]
방문체크를 통한 순열 생성
package day0323_백트래킹;
import java.util.Arrays;
//visited
public class 순열_3 {
static int[] nums;
static int N;
static boolean[] visited; //해당 원소 사용 유무
static int[] result; //순열결과 저장
public static void main(String[] args) {
nums = new int[] {0,1,2};
N = nums.length;
result = new int[N];
visited = new boolean[N];
perm(0);
}
public static void perm(int idx) {
if(idx == N) {
System.out.println(Arrays.toString(result));
return;
}
//모든 요소를 반복문을 통해 돌면서
//사요하지 않은 원소가 있다면 결과에 넣고 사용했다고 표시도 하고
//다음 차례로 내려가 본다.
for(int i = 0 ; i<N; i++) {
//1. 원소를 사용했는지 쳌
if(visited[i]) continue;
//요기에다가 작성 (실행된다는 것은 안쓴 원소다)
result[idx] = nums[i];
visited[i] = true; //해당원소 썼어요~
perm(idx+1); //내려가
visited[i] = false; //다시 원상복구
}
}
}
//
[0, 1, 2]
[0, 2, 1]
[1, 0, 2]
[1, 2, 0]
[2, 0, 1]
[2, 1, 0]
비트마스킹을 이용한 순열
package day0323_백트래킹;
import java.util.Arrays;
//bitmasking
public class 순열_4 {
static int[] nums;
static int N;
static int[] result; //순열결과 저장
public static void main(String[] args) {
nums = new int[] {0,1,2};
N = nums.length;
result = new int[N];
perm(0, 0);
}
//idx : 현재 판단하고 있는 위치
//visited: 사용한 원소를 기록하기 위한 정수
public static void perm(int idx, int visited) {
// if(visited == (1<<N)-1)) 이런식의 표현도 가넝 (우마이)
if(idx == N) {
System.out.println(Arrays.toString(result));
return;
}
//모든 원소를 돌면서 판단해~
for(int i = 0 ; i<N; i++) {
//현재 내가 i번째 원소를 사용했다면 쳐내
if((visited & (1<<i)) != 0 ) continue;
result[idx] = nums[i]; //결과저장
perm(idx+1, visited | (1<<i));
}
}
}
//
[0, 1, 2]
[0, 2, 1]
[1, 0, 2]
[1, 2, 0]
[2, 0, 1]
[2, 1, 0]