https://www.acmicpc.net/problem/14225
14225번: 부분수열의 합
수열 S가 주어졌을 때, 수열 S의 부분 수열의 합으로 나올 수 없는 가장 작은 자연수를 구하는 프로그램을 작성하시오. 예를 들어, S = [5, 1, 2]인 경우에 1, 2, 3(=1+2), 5, 6(=1+5), 7(=2+5), 8(=1+2+5)을 만들
www.acmicpc.net
코드설명
브루트포스인데 재귀를 활용해야만 시간초과를 벗어날 수 있는 문제입니다.
아래의 로직처럼
각 구간을 더하지않고 다음 단계로 넘어가고,
각 구간을 더하고 다음 단계로 넘어가는 로직
이 것을 구현해주면 됩니다.
또한 추가로 알아야할점은 부분수열이란 원래 수열의 순서를 유지한 수열을 부분수열이라고 합니다. (순서가 바뀌면 안된다는 의미입니다.)
{1,2,3,4,5} 가 있다고 가정하면,
{0}, {1}, {1,2}, {1,3}, {1,4} {1,5}, {1, 2,3} ... 이런식으로 빠르게 해결할 수 있습니다.
재귀를 활용하여 사용한 코드 같은경우 각 순열에서 더하냐, 더하지 않냐의 2가지 방식으로만 진행되므로 불필요한 수열 연산이 더해지지 않으므로 시간초과가 걸리지 않는것입니다.
조금 더 코드적으로 생각해보면,
모든 경우가 더해지지 않는경우를 먼저 계산한뒤에 마지막 정수인 5 부터 더해지면서 진행될 것입니다.
{5} , {4,5} ... 이렇게 되겠습니다. 코드가 다 들어간뒤 + arr[level]으로 실행하기에 그렇습니다.
public static void simulate(int level, int sum) {     if(level == N) {         sumTemp[sum] = true;         return ;     }       simulate(level + 1, sum);     simulate(level+1, sum +arr[level]);   } 
처음에 for문과 재귀를 함께 활용하여 진행해보았는데 아래와 같이 진행할경우 중복된 연산이 발생하였고,
( 예로들면, 1 + 2 이 진행된 이후에 나중에 2 + 1 이 for문으로 인하여 다시 반복되는 경우가 있습니다. )
그리하여서 idx를 활용하여 앞에서 사용된 수열은 사용하지 않기에 idx를 처리하였더니 통과는 하지만, 훨씬 많은 연산이 필요하기에 시간과 메모리가 3~4배 이상 차이납니다.
    public static void simulate(int idx, int level, int sum) {     	if(level > N) {     		return ;     	}     	hashset.add(sum);     	for(int i=idx; i<N;i++) {     		if(visited[i] == false) {     			visited[i] = true;     			simulate(i+1, level+1, sum +arr[i]);     			visited[i] = false;     		}     	}     	     } 
단순하게 재귀를 활용하여 더하는 경우와 더하지 않는경우를 나누어서 재귀가 계속 실행되는 방식으로 하는것이 좋아보입니다.
코드
재귀를 활용하여 불필요한 연산, 메모리 사용하지않음
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.StringTokenizer;   public class Main { 	 	public static int N; 	public static int answer = 0; 	public static int[] arr; 	public static boolean[] visited; 	public static boolean[] sumTemp = new boolean[20*100001];     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());     	arr = new int[N];     	visited = new boolean[N];     	st = new StringTokenizer(br.readLine());     	for(int i=0;i<N;i++) {     		arr[i] = Integer.parseInt(st.nextToken());     	}     	simulate(0, 0);     	     	for(int i=1; i<100000*20;i++) {     		if(sumTemp[i] == false) {     			System.out.println(i);     			break;     		}     	}     }          public static void simulate(int level, int sum) {     	if(level == N) {         	sumTemp[sum] = true;     		return ;     	}     	     	simulate(level + 1, sum); 		simulate(level+1, sum +arr[level]);     	     }           } 
hashset -> 배열 에다가 모든 순열을 구하면서 진행해본 코드 여전히 시간초과 ( 이코드는 idx사용안했서 중복연산하여 시간초과 발생)
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.StringTokenizer;   public class Main { 	 	public static int N; 	public static int answer = 0; 	public static int[] arr; 	public static boolean[] visited; 	public static boolean[] sumTemp = new boolean[20*100001];     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());     	arr = new int[N];     	visited = new boolean[N];     	st = new StringTokenizer(br.readLine());     	for(int i=0;i<N;i++) {     		arr[i] = Integer.parseInt(st.nextToken());     	}     	simulate(0, 0);     	     	for(int i=1; i<100000*20;i++) {     		if(sumTemp[i] == false) {     			System.out.println(i);     			break;     		}     	}     }          public static void simulate(int level, int sum) {     	if(level > N) {     		return ;     	}     	sumTemp[sum] = true;     	for(int i=0; i<N;i++) {     		if(visited[i] == false) {     			visited[i] = true;     			simulate(level+1, sum +arr[i]);     			visited[i] = false;     		}     	}     	     }           } 
이후에 idx를 활용하여 중복되어 더해지는 값을 제거하여 solve
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.HashSet; import java.util.StringTokenizer;   public class Main { 	 	public static int N; 	public static int answer = 0; 	public static int[] arr; 	public static boolean[] visited; 	public static int[] answerArr; 	public static HashSet<Integer> hashset = new HashSet<Integer>();     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());     	arr = new int[N];     	visited = new boolean[N];     	st = new StringTokenizer(br.readLine());     	for(int i=0;i<N;i++) {     		arr[i] = Integer.parseInt(st.nextToken());     	}     	simulate(0, 0, 0);     	     	for(int i=1; i<100000*20;i++) {     		if(!hashset.contains(i)) {     			System.out.println(i);     			break;     		}     	}     }          public static void simulate(int idx, int level, int sum) {     	if(level > N) {     		return ;     	}     	hashset.add(sum);     	for(int i=idx; i<N;i++) {     		if(visited[i] == false) {     			visited[i] = true;     			simulate(i+1, level+1, sum +arr[i]);     			visited[i] = false;     		}     	}     	     }           } 
처음에 시간초과가 난 코드 ( 이코드는 idx사용안했서 중복연산하여 시간초과 발생)
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.HashSet; import java.util.StringTokenizer;   public class Main { 	 	public static int N; 	public static int answer = 0; 	public static int[] arr; 	public static boolean[] visited; 	public static int[] answerArr; 	public static HashSet<Integer> hashset = new HashSet<Integer>();     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());     	arr = new int[N];     	visited = new boolean[N];     	st = new StringTokenizer(br.readLine());     	for(int i=0;i<N;i++) {     		arr[i] = Integer.parseInt(st.nextToken());     	}     	simulate(0, 0);     	     	for(int i=1; i<100000*20;i++) {     		if(!hashset.contains(i)) {     			System.out.println(i);     			break;     		}     	}     }          public static void simulate(int level, int sum) {     	if(level > N) {     		return ;     	}     	hashset.add(sum);     	for(int i=0; i<N;i++) {     		if(visited[i] == false) {     			visited[i] = true;     			simulate(level+1, sum +arr[i]);     			visited[i] = false;     		}     	}     	     }           } 
'알고리즘 > 백준' 카테고리의 다른 글
| [백준] 16197 두 동전 - BFS + 아이디어 JAVA (0) | 2023.09.08 | 
|---|---|
| [백준] 15658 연산자 끼워넣기 (2) - 브루트포스(재귀) JAVA (0) | 2023.09.08 | 
| [백준] 6603 로또 - 백트래킹 JAVA (0) | 2023.09.07 | 
| [백준] 1339 단어 수학 - 백트래킹 + 그리디 JAVA (0) | 2023.09.07 | 
| [백준] 2529 부등호 - 브루트포스 + 구현 JAVA (0) | 2023.09.07 |