크기가 n인 집합이 주어졌을 때, 그 부분집합의 갯수는 2^n이다.
이 문제에서 최대 N은 40이기 때문에 2^40=1.0995116e+12
어느정도인지는 모르겠고, 1초 안에 해결할 수 없는 크기인건 알겠다.
따라서 이 문제는 분할하여 풀어야한다.
2^20은 약 1000000으로 1초안에 계산할 수 있기 때문에
주어진 집합을 반으로 쪼개어서 각각 구한 후 풀이한다.
풀이과정은 다음과 같다.
[ A, B, C, D, E, F ] 집합이 주어졌을 때,
[ A, B C ] [ D, E, F ] 로 쪼갠다.
분할된 두 집합의 부분집합을 구한 후(공집합 포함) 두 분할을 활용하면 문제를 풀이할 수 있다.
여기서 공집합을 포함하는 이유는 앞의 부분집합의 공집합을 선택할지라도 뒤에서 공집합이 아닌 집합을 선택하면 결과적으로 그 부분집합은 공집합이 아니기 때문이다.
따라서, 공집합 제외라는 예외처리는 둘다 0인 경우를 빼야하는데, S가 0으로 주어졌을 때 공집합을 함께 구하게 되므로 1만큼만 빼주면 된다.
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Collections; import java.util.StringTokenizer; public class Main { static void makePre(int[] arr, int idx, int e, int sum, ArrayList<Integer> array) { if(idx>= e)//끝까지 다 돌았다면 { array.add(sum);//추가 return; } makePre(arr, idx+1, e, sum, array);//현재 인덱스를 포함할 때 makePre(arr, idx+1, e, sum+arr[idx], array);//현재 인덱스를 포함하지 않을 때 } public static void main(String[] args) throws IOException { BufferedReader br=new BufferedReader(new InputStreamReader(System.in)); StringTokenizer stringTokenizer=new StringTokenizer(br.readLine()); int N= Integer.parseInt(stringTokenizer.nextToken()); int S=Integer.parseInt(stringTokenizer.nextToken()); int[] arr=new int[N]; stringTokenizer=new StringTokenizer(br.readLine()); int k=0; while(stringTokenizer.hasMoreTokens()) arr[k++]=Integer.parseInt(stringTokenizer.nextToken()); ArrayList<Integer> first_arr=new ArrayList<>(); ArrayList<Integer> second_arr=new ArrayList<>(); makePre(arr,0,N/2, 0, first_arr); makePre(arr,N/2, arr.length, 0, second_arr); Collections.sort(first_arr); Collections.sort(second_arr); int left=0; int right=second_arr.size()-1; long ans=0; while(left < first_arr.size() && right >=0) { long ls=first_arr.get(left); long rs=second_arr.get(right); if(ls+rs == S) {//S와 일치하면, 각 분할 집합에서 해당 합을 지니는 원소의 개수를 셈 long lc=0; while( left<first_arr.size() && first_arr.get(left)== ls) { lc++; left++; } long rc=0; while( right >=0 && second_arr.get(right)== rs) { rc++; right--; } ans+=lc*rc; } if(ls+rs > S) right--; if(ls+rs < S) left++; } if(S==0) { ans-= 1; } System.out.println(ans); } }
'이론 > 문제풀이' 카테고리의 다른 글
[백준 알고리즘 1464] 1로 만들기 - java(Dynamic Programming) (0) | 2018.09.12 |
---|---|
[백준 알고리즘 - 2632] 피자판매 - java/완전탐색(재귀호출) (0) | 2018.09.09 |
[백준 알고리즘 - 1261] 알고스팟 (java, BFS) (0) | 2018.09.04 |
[백준 알고리즘 -1182] 부분집합의 합 - java(재귀호출/부제:오랜만에 삽질일기) (0) | 2018.09.01 |
[백준 알고리즘 - 1987] 알파벳 - java(백트래킹) (0) | 2018.08.31 |