1182번 부분수열의 합1은 주어진 수열의 크기가 최대 20이기 때문에 brute force 방법을 사용하여 존재할 수 있는 모든 부분 수열을 생성한 후 계산해도 O(2N) = 220-1 = 1,048,575 은 시간초과가 되지 않는다.
하지만 1208번 문제는 길이가 2배 늘어났기 때문에 O(2N) = 24 -1 = 0-1 = 1,099,511,627,776 은 1초라는 시간안에 해결될 수 없다.
완전 탐색을 사용하면 시간 초과가 나는 경우 [중간에서 만나기 (meet in the middle)] 방법을 대안으로 생각해낼 수 있다.

sub_sum() 함수를 통해 수행하며, 결과적으로 2개의 합 배열 s1, s1가 생성된다)Q. 왜 s1는 오름차순 정렬을 하고, s2는 내림차순 정렬을 할까?
A. 투 포인터 기법을 통해 효율적으로 합이 s가 되는 경우를 찾기 위해서이다.
if s1[p1] + s2[p2] < s :
s1값을 증가시키기 위해 p1 += 1
# 오름차순 s1를 증가시키면 합이 증가하는 효과
elif s1[p1] + s2[p2] > s :
s2값을 감소시키기 위해 p2 += 1
# 내림차순 s2를 증가시키면 합이 감소하는 효과
elif s1[p1] + s2[p2] == s :
cnt += 1
p1 += 1
# 중간에서 만나기 - 1208번 - 부분수열의 합2
from itertools import combinations
# 1. 수열을 입력받은 후 절반으로 나눔
m, s = map(int, input().split())
arr = list(map(int, input().split()))
if m == 1:
if arr[0] == s:
print(1)
else:
print(0)
exit()
a1 = arr[:m // 2]
a2 = arr[m // 2:]
# 2. 모든 부분수열의 조합을 생성 -> 그 부분수열의 합을 새로운 배열에 저장
def sub_sum(array):
sums = []
n = len(array)
for i in range(n + 1):
for comb in combinations(array, i):
sums.append(sum(comb))
return sums
s1 = sub_sum(a1)
s2 = sub_sum(a2)
s1.sort()
s2.sort(reverse=True)
# 3. 투포인터 방식으로 모든 경우의 수 구하기
p1, p2, cnt = 0, 0, 0 # 포인터
while p1 < len(s1) and p2 < len(s2):
current_sum = s1[p1] + s2[p2]
if current_sum == s:
c1 = 1 # s1에서 s1[p1]의 개수를 저장하는 변수
c2 = 1 # s2에서 s2[p2]의 개수를 저장하는 변수
p1 += 1
p2 += 1
while p1 < len(s1) and s1[p1] == s1[p1 - 1]:
c1 += 1
p1 += 1
while p2 < len(s2) and s2[p2] == s2[p2 - 1]:
c2 += 1
p2 += 1
cnt += c1 * c2
elif current_sum < s:
p1 += 1
else:
p2 += 1
if s == 0:
cnt -= 1 # 아무것도 선택 안한 경우 제외하기
print(cnt)
시간복잡도 (m = n/2)
= 2m + 2m * log 2m + 2m
= 각 수열에서 가능한 모든 부분수열 만들기 + 합이 저장된 수열을 정렬 + 투포인터
= n * 2n/2