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가 주어졌을 때, 수를 고르는 모든 방법을 구하는 프로그램을 작성하시오.
입력
입력은 여러 개의 테스트 케이스로 이루어져 있다. 각 테스트 케이스는 한 줄로 이루어져 있다. 첫 번째 수는 k (6 < k < 13)이고, 다음 k개 수는 집합 S에 포함되는 수이다. S의 원소는 오름차순으로 주어진다.
입력의 마지막 줄에는 0이 하나 주어진다.
출력
각 테스트 케이스마다 수를 고르는 모든 방법을 출력한다. 이때, 사전 순으로 출력한다.
각 테스트 케이스 사이에는 빈 줄을 하나 출력한다.
예제 입력 1
7 1 2 3 4 5 6 7
8 1 2 3 5 8 13 21 34 0
예제 출력 1
1 2 3 4 5 6
1 2 3 4 5 7
1 2 3 4 6 7
1 2 3 5 6 7
1 2 4 5 6 7
1 3 4 5 6 7
2 3 4 5 6 7
1 2 3 5 8 13
1 2 3 5 8 21
1 2 3 5 8 34
1 2 3 5 13 21
1 2 3 5 13 34
1 2 3 5 21 34
1 2 3 8 13 21
1 2 3 8 13 34
1 2 3 8 21 34
1 2 3 13 21 34
1 2 5 8 13 21
1 2 5 8 13 34
1 2 5 8 21 34
1 2 5 13 21 34
1 2 8 13 21 34
1 3 5 8 13 21
1 3 5 8 13 34
1 3 5 8 21 34
1 3 5 13 21 34
1 3 8 13 21 34
1 5 8 13 21 34
2 3 5 8 13 21
2 3 5 8 13 34
2 3 5 8 21 34
2 3 5 13 21 34
2 3 8 13 21 34
2 5 8 13 21 34
3 5 8 13 21 34
풀이 .
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Main {
static BufferedReader br = null;
static StringTokenizer st = null;
static StringBuilder sb = null;
static int K;
static int[] S = null;
static int[] lotto = null;
static boolean[] check = null;
public static void dfs(int dept) {
if(dept == 6) {
for(int i = 0; i < 6; i++) {
sb.append(lotto[i] + " ");
}
sb.append("\n");
return;
}
for(int i = 0; i < K; i++) {
if(!check[i] && S[i] > lotto[dept - 1]) {
lotto[dept] = S[i];
check[i] = true;
dfs(dept + 1);
check[i] = false;
}
}
}
public static void main(String[] args) throws IOException {
br = new BufferedReader(new InputStreamReader(System.in));
sb = new StringBuilder();
while(true) {
st = new StringTokenizer(br.readLine());
K = Integer.parseInt(st.nextToken());
if(K == 0) break;
S = new int[K];
lotto = new int[6];
check = new boolean[K];
for(int i = 0; i < K; i++) {
S[i] = Integer.parseInt(st.nextToken());
}
for(int i = 0; i < K; i++) {
lotto[0] = S[i];
check[i] = true;
dfs(1);
check[i] = false;
}
sb.append("\n");
}
System.out.println(sb.toString());
}
}
백트래킹을 통해 가능한 모든 조합을 만들어 낸다.
주의할 점은, 오름차순 조합에 대해서만 출력하는 것이다.
즉, 같은 숫자들로 만들어진 조합이라도 오름차순일 때만 정답으로 친다.
ex ) 1, 2, 3, 4, 5, 6 으로 만들어진 여러 조합들 (1, 2, 3, 4, 5, 6), ... , (6, 5, 4, 3, 2, 1) 중에서는 (1, 2, 3, 4, 5, 6) 하나만 정답으로 친다는 것.
이를 위해 현재 lotto[]의 마지막 원소보다 값이 큰 경우에만 재귀를 하도록 하는 조건을 추가했다.
이렇게하면 오름차순이 유지되지 않는 조합은 만드는 것을 중단하기 때문에 모든 조합을 만들어놓고 오름차순 여부를 검사하는 것보다 더 빠르게 실행할 수 있다.
사실 이것보다 더 효율적인 방법이 있긴 하다.
사전에 S[]를 미리 오름차순 정렬해놓고,
이전에 입력받은 숫자의 S[] 에서의 인덱스를 기억하며 그 인덱스보다 큰 인덱스를 갖는 S[]의 원소만 재귀하도록 하는 것이다.
public static void dfs(int idx, int dept) {
if(dept == 6) {
for(int i = 0; i < 6; i++) {
sb.append(lotto[i] + " ");
}
sb.append("\n");
return;
}
for(int i = idx; i < K; i++) {
if(!check[i]) {
lotto[dept] = S[i];
check[i] = true;
dfs(i + 1, dept + 1);
check[i] = false;
}
}
}
이렇게 하면 헛도는 반복마저도 하지 않게 될 수 있으니 더 빨리 처리할 수 있게 된다.
(사실 그렇게 큰 차이는 아닌 듯 하다.)
그렇게 큰 차이도 아니고 코드를 이해하기가 더 쉬운 위유로 첫번째 방법이 더 나은 듯 하다.
'알고리즘 문제 > 백준 온라인 저지' 카테고리의 다른 글
[BOJ] 2003 - 수들의 합 2 JAVA (0) | 2021.02.15 |
---|---|
[BOJ] 1182 - 부분수열의 합 JAVA (0) | 2021.02.15 |
[BOJ] 1987 - 알파벳 JAVA (0) | 2021.02.15 |
[BOJ] 2580 - 스도쿠 JAVA (0) | 2021.02.15 |
[BOJ] 1759 - 암호 만들기 JAVA (0) | 2021.02.14 |