https://www.acmicpc.net/problem/16508
16508번: 전공책
곧 졸업을 앞둔 민호는 대학교 생활 동안 구매만 해놓고 한 번도 펴보지 않은 전공책에 먼지가 쌓여 있는 것을 보고는, 이 책들을 어떻게 처리해야 할지 고민 중이다. 열심히 고민한 끝에 민호는
www.acmicpc.net
문제풀이
https://gunjoon.tistory.com/100
완전탐색 문제입니다.
문제의 조건에
- 민호가 만들고자 하는 단어를 의미하는 문자열 T (1 ≤ |T| ≤ 10), 민호가 가진 전공책의 개수를 의미하는 정수 N (1 ≤ N ≤ 16)이 주어진다. 전공책 가격을 의미하는 정수 Ci (10,000 ≤ Ci ≤ 100,000)와 제목을 의미하는 문자열 Wi (1 ≤ |Wi| ≤ 50)가 주어진다. Wi는 항상 대문자이다.
위의 조건을 보고 완전탐색을 돌려도 시간초과가 날것 같지 않다는 생각에 완전탐색을 구현했습니다.
( 아래의 코드에 틀린 코드가 있습니다. )처음에 틀렸었던 이유는 완전탐색을 진행하면서 O(N^2)으로 진행하여 최악의 경우 16^16 의 시간이 주어져서 시간초과가 났습니다.
코드로직입니다.알파벳 26 개의 배열 2개를 선언합니다. ( 알파벳 관련 문제들에 은근히 이런 방식의 접근이 많이 사용되는것 같습니다. )1. T_count[26] : 구하고자하는 문자열의 알파벳의 개수, select_alphabet[26] : 전공책에서 뽑아온 알파벳 개수2. T_count[26]에 구하고자 하는 문자열 알파벳을 T_count[26]에 넣어주기 위해 ++ 처리합니다. (만약 3개라면 3번 더해집니다.)3. 각 전공책을 돌면서 전공책의 모든 알파벳을 select_alphabet[26]에 넣어주기 위해 ++ 처리합니다.
4. 각 전공책을 안넣는 경우도 있기에 select_alphabet[26]을 만나더라도 +0 처리하는 경우도 있습니다.
5. T_count[26] 의 알파벳 개수보다 select_alphabet[26]의 개수가 더 작은것이 하나라도 존재한다면, 해당 문자열은 완성하지 못합니다.
코드
시간초과가 난코드입니다.
완전탐색를 진행하면서 너무 많은 배열과 방문처리를 했기에 시간초과가 납니다.
아래 문제의 시간복잡도를 계산해보면
모든 전공서적 N (최대 16 )의 경우의 수를 16번 모두 16 ^ 16 = 18,446,744,073,709,551,616 이기에 시간초과가 나옵니다. ( 일반적으로 1 억만 넘어가도 나옴 )
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.*; public class Main { public static String T; public static int N; public static int price; public static int T_index=0; public static Node[] Node; public static boolean[][] visited; public static Node[] T_Node; public static int answer = Integer.MAX_VALUE; public static void main(String[] args) throws IOException{ BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); T = st.nextToken(); T_Node = new Node[T.length()]; st = new StringTokenizer(br.readLine()); N = Integer.parseInt(st.nextToken()); Node = new Node[N]; visited = new boolean[N][50]; for(int i=0;i<N;i++) { st = new StringTokenizer(br.readLine()); int a = Integer.parseInt(st.nextToken()); String b = st.nextToken(); Node[i] = new Node(a, b, i); } simulate(0); if(answer == Integer.MAX_VALUE) System.out.println("-1"); else System.out.println(answer); } public static void simulate(int level) { // System.out.println("======================"); if(level == T.length()) { boolean[] temp = new boolean[N]; int sum = 0; for(int i=0;i<level;i++) { if(temp[T_Node[i].nodeIndex] == false) { sum += T_Node[i].price; // System.out.println(" "+T_Node[i].price+ " "+T_Node[i].title); temp[T_Node[i].nodeIndex] = true; } } answer = Math.min(answer, sum); return; } for(int i=0;i<N;i++) { Node temp = Node[i]; int price = temp.price; String title = temp.title; if(T_Node[level] != null) continue; //이미 방문한적이 있다면 다음 문자로 넘어감. for(int j=0;j<title.length();j++) { if(visited[i][j] == true) continue; if( T.charAt(level) == title.charAt(j)) { visited[i][j] = true; T_Node[level] = temp; simulate(level + 1); visited[i][j] = false; T_Node[level] = null; } } } } } class Node{ int price; String title; int nodeIndex; public Node(int price, String title, int nodeIndex) { this.price = price; this.title = title; this.nodeIndex = nodeIndex; } }
정답코드
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.*; public class Main { public static String T; public static int N; public static Node[] Node; public static int[] T_count = new int[26]; //알파벳 26개 public static int[] select_alphabet = new int[26]; //알파벳 26개 public static int answer = Integer.MAX_VALUE; public static void main(String[] args) throws IOException{ BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); T = st.nextToken(); st = new StringTokenizer(br.readLine()); N = Integer.parseInt(st.nextToken()); // T 목표 문자열 ( 민호가 만들고자 하는 문자열 )을 돌면서 문자열의 문자들을 구하기 위해 += 1 처리 for(int i=0;i<T.length();i++) { T_count[T.charAt(i) - 'A']++; } Node = new Node[N]; for(int i=0;i<N;i++) { st = new StringTokenizer(br.readLine()); int a = Integer.parseInt(st.nextToken()); String b = st.nextToken(); Node[i] = new Node(a, b); } simulate(0, 0); if(answer == Integer.MAX_VALUE) { System.out.println("-1"); }else { System.out.println(answer); } } public static void simulate(int level, int total) { // System.out.println("total:"+total); //만약 모든 전공책을 다 돌았다면 if(level == N) { boolean flag = true; //T_count를 통해서 구하고자하는 문자의 개수를 구했고, 그 문자보다 select_alphabet이 모두 커야 해당 문자를 구한 것입니다. for(int i=0; i<26;i++) { if( T_count[i] > select_alphabet[i] ) { flag = false; break; } } if( flag == true) { answer = Math.min(answer, total); } return ; } // level 번째의 전공책의 문자열을 돌면서 문자열의 문자들을 모두 방문 처리해줍니다. for(int i=0;i<Node[level].title.length();i++) { //해당 전공책을 읽음처리합니다. select_alphabet[Node[level].title.charAt(i) - 'A']++; } simulate(level + 1, total + Node[level].price); // level 번째의 전공책을 선택하지 않는다면, 이전 Level에서 위의 코드를 보면 ++ 한 개수를 -- 를 함으로써 다시 선택안한것으로 수정합니다. for(int i=0;i<Node[level].title.length();i++) { select_alphabet[Node[level].title.charAt(i) - 'A']--; } simulate(level + 1, total); } } class Node{ int price; String title; public Node(int price, String title) { this.price = price; this.title = title; } }
'알고리즘 > 백준' 카테고리의 다른 글
[백준] 2961 도영이가 만든 맛있는 음식 - 완전탐색 (DFS) JAVA (0) | 2023.07.15 |
---|---|
[백준] 14620 꽃길 - 완전탐색 JAVA (0) | 2023.07.15 |
[백준] 16937 두 스티커 - 완전탐색 + 아이디어 JAVA (0) | 2023.07.14 |
[백준] 17626 Four Squares - 동적계획법(DP, Dynamic Programming) JAVA (0) | 2023.07.13 |
[백준] 2503 숫자 야구 - 완전탐색(DFS) + 아이디어 JAVA (0) | 2023.07.12 |