
완전 탐색은 존재하는 모든 답을 하나씩 검사하므로, 검사에 걸리는 시간은 답의 수에 비례한다. 최대 크기의 입력을 가정했을 때 답의 개수를 계산하고 그것을 제한시간 내에 생성할 수 있는지를 가늠해야한다. 제한 시간을 초과할 것 같다면 다른 알고리즘을 사용해 풀어야 한다.
가능한 모든 답의 후보를 만드는 과정을 여러개의 선택으로 나누며 각 선택은 답의 후보를 만드는 과정의 한 조각이 된다.
그중 하나의 조각을 선택해 답의 일부를 만들고 나머지 답을 재귀 호출을 통해 완성한다.
조각이 하나밖에 남지 않은경우 혹은 하나도 남지 않는 경우에는 답을 생성했으므로 이것을 기저 사례로 선택해 처리한다.
입력의 크기가 작은 경우
입력의 크기가 작으면 모든 경우를 탐색해도 실행시간이 적당하게 소요될 수 있습니다. 따라서, 입력의 크기가 작은 경우에는 완전탐색을 사용하는 것이 적절합니다.
최적해가 유일한 경우
최적해가 유일한 경우, 즉 하나의 결과만 존재하는 경우에는 완전탐색을 사용하는 것이 적절합니다.
탐색해야 하는 경우의 수가 적은 경우
모든 경우를 탐색해야 하므로 탐색해야 하는 경우의 수가 많을수록 실행시간이 증가합니다. 따라서, 경우의 수가 적을 경우에는 완전탐색을 사용하는 것이 적절합니다.
부분해가 쉽게 구할 수 있는 경우
모든 경우를 탐색하지 않고도 최적해에 가까운 부분해를 구할 수 있는 경우에는 완전탐색을 사용하지 않아도 됩니다.
가지치기 (Pruning)가 가능한 경우
가지치기를 통해 실행시간을 줄일 수 있는 경우에는 완전탐색을 사용하는 것이 적절합니다.
문제란 항상 수행해야 할 작업과 그 작업을 적용할 자료의 조합을 의미한다. 이 문제를 해결하기 위해서 여러개의 부분문제로 분할하여 해결해야 한다.
부분문제란 전체적인 문제를 해결하기 위해 분할한 작은 문제를 말한다. 부분 문제들은 전체적인 문제와 유사하며 같은 알고리즘을 적용하여 해결한다.
https://algospot.com/judge/problem/read/PICNIC
안드로메다 유치원 익스프레스반에서는 다음 주에 율동공원으로 소풍을 갑니다. 원석 선생님은 소풍 때 학생들을 두 명씩 짝을 지어 행동하게 하려고 합니다. 그런데 서로 친구가 아닌 학생들끼리 짝을 지어 주면 서로 싸우거나 같이 돌아다니지 않기 때문에, 항상 서로 친구인 학생들끼리만 짝을 지어 줘야 합니다.
각 학생들의 쌍에 대해 이들이 서로 친구인지 여부가 주어질 때, 학생들을 짝지어줄 수 있는 방법의 수를 계산하는 프로그램을 작성하세요. 짝이 되는 학생들이 일부만 다르더라도 다른 방법이라고 봅니다. 예를 들어 다음 두 가지 방법은 서로 다른 방법입니다.
(태연,제시카) (써니,티파니) (효연,유리)
(태연,제시카) (써니,유리) (효연,티파니)
입력의 첫 줄에는 테스트 케이스의 수 C (C <= 50) 가 주어집니다. 각 테스트 케이스의 첫 줄에는 학생의 수 n (2 <= n <= 10) 과 친구 쌍의 수 m (0 <= m <= n*(n-1)/2) 이 주어집니다. 그 다음 줄에 m 개의 정수 쌍으로 서로 친구인 두 학생의 번호가 주어집니다. 번호는 모두 0 부터 n-1 사이의 정수이고, 같은 쌍은 입력에 두 번 주어지지 않습니다. 학생들의 수는 짝수입니다.
각 테스트 케이스마다 한 줄에 모든 학생을 친구끼리만 짝지어줄 수 있는 방법의 수를 출력합니다.
3
2 1
0 1
4 6
0 1 1 2 2 3 3 0 0 2 1 3
6 10
0 1 0 2 1 2 1 3 1 4 2 3 2 4 3 4 3 5 4 5
1
3
4
문제를 보고 도대체 이게 무슨말이지? 하고 한참을 해메었던것 같다.
때문에 문제의 조건들과 입출력되는 값들이 무엇을 뜻하는지부터 한줄한줄 적으며 정리하는 단계를 거쳤다.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Picnic {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int c = Integer.parseInt(br.readLine());// 테스트 케이스 수
// 테스트 케이스의 수 만큼 테스트 케이스 생성
for(int i = 0; i < c; i++){
int n = 0; // 학생수
int m = 0; // 친구 쌍 수
boolean[][] friends = new boolean[n][n];
boolean[] check = new boolean[n];
while(n < 2 || n >10 || m < 0 || m > n*(n-1)/2){
System.out.print("n 입력 : ");
n = Integer.parseInt(br.readLine());
System.out.print("m 입력 : ");
m = Integer.parseInt(br.readLine());
}
for(int j = 0; j < m; j++){
System.out.println(m);
System.out.print("first 입력 : ");
int first = Integer.parseInt(br.readLine());
System.out.print("second 입력 : ");
int second = Integer.parseInt(br.readLine());
friends[first][second] = true;
friends[second][first] = true;
}
System.out.println(picnic(check,friends,n,n/2));
}
}
static int picnic(boolean[] check,boolean[][] friends,int n,int count){
if(count == 0){ // 기저사례 : 선택한 그룹이 n/2가 된 경우 return ex)학생이 6명일때 3그룹이 생기면 return
return 1;
}
int k = 0; // 시작할 번호 지정
for(int i = 0; i < n; i++){
if(check[i] == false){ // 이전에 선택된적 없는 번호 선택
k=i;
break;
}
}
int result = 0; // 기저사례에 도착할 때 마다 +1 되어 횟수 카운트
for(int i = 0; i < n; i++){
if(check[i] == false && friends[k][i] == true){ // 시작번호가 선택된적없고, 두 번호가 친구리스트에 있는경우
check[k] = true; // 체크리스트에 선택된 번호 체크
check[i] = true; // 체크리스트에 선택된 번호 체크
count--; // 한 그룹이 생길 때 마다 count에서 -1
result += picnic(check, friends, n, count);
count++; // 기저사례 도착하면 count +1
check[i] = false; // 마지막에 생긴 그룹의 선택을 해제 해 줘야 다른 경우의 수를 찾을 수 있음
check[k] = false;
}
}
return result;
}
}