Toolofv 님의 블로그

[Python] 백준 - 1450 냅색문제 본문

Algorithm

[Python] 백준 - 1450 냅색문제

Toolofv 2024. 6. 19. 13:35
 

백준 - 1450 냅색문제

첫째 줄에 N과 C가 주어진다. N은 30보다 작거나 같은 자연수, C는 109보다 작거나 같은 음이 아닌 정수이다. 둘째 줄에 물건의 무게가 주어진다. 무게도 109보다 작거나 같은 자연수이다.

www.acmicpc.net

 

 

문제

세준이는 N개의 물건을 가지고 있고, 최대 C만큼의 무게를 넣을 수 있는 가방을 하나 가지고 있다.

N개의 물건을 가방에 넣는 방법의 수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 N과 C가 주어진다. N은 30보다 작거나 같은 자연수, C는 109보다 작거나 같은 음이 아닌 정수이다. 둘째 줄에 물건의 무게가 주어진다. 무게도 109보다 작거나 같은 자연수이다.

출력

첫째 줄에 가방에 넣는 방법의 수를 출력한다.

 

문제해결방법- (구글링 참조)

 

 

1. 처음에는 문제가 이해가 안되었다. 안 넣는 것도 방법의 수에 포함이 되는 모양.

2. Meet in the middle 알고리즘 

 

 

Meet in the middle 이란?

 

 

한번에 연산하기 어려운 문제를 둘로 나누어 시간적인 단축을 꾀하는 알고리즘

 

 

1) 분할없이 경우의 수를 카운트한다면 $2^{n}$ 의 시간복잡도를 갖는다. 최대 $O(2^{30})$이다.

2) 분할하여 카운트하면 $2^{15}$ , $2^{15}$로 나뉘어져 $O(2\times 2^{15})$  의 연산만을 하게되어 시간복잡도가 현저히 줄어든다.

 

--> 가방에 c만큼의 무게를 넣을 수 있다면 'left 로 분할한 경우의 수에 해당하는 물건들의 합'을 구한 후, 'c left 각 차이'만큼 'right 에 담겨있는 경우의 수에 해당하는 물건들의 합'을 담을 수 있을 것이다.

 

 

 

3. 이분탐색 알고리즘

 

1) 여기에서 이분탐색 알고리즘이 활용되는데, 이분탐색은 딱 정해진 target을 찾는 정석적인 알고리즘 외에도 우측탐색, 좌측탐색의 방법으로 같은 원소 여러 개target일 경우, 가장 좌측이거나 가장 우측을 구할 수도 있다.

 

혹은 백준 1654 - 랜선자르기, 백준 2805 - 나무자르기 문제처럼 최댓값, 최솟값을 찾는 응용을 할 수도 있다.

 

-> mid 인덱스에 해당하는 값이 같을 때(mid == target) 그 값을 출력하지 않고 l mid+1 로 하느냐, r mid-1 로 하느냐에 따라 우측탐색, 좌측탐색이 된다.

 

 

[Python] 백준 - 1654 랜선 자르기

문제집에서 시간을 보내던 오영식은 박성원의 부름을 받고 급히 달려왔다. 박성원이 캠프 때 쓸 N개의 랜선을 만들어야 하는데 너무 바빠서 영식이에게 도움을 청했다.이미 오영식은 자체적으

toolofv.tistory.com

 

 

[Python] 백준 - 2805 나무 자르기

문제상근이는 나무 M미터가 필요하다. 근처에 나무를 구입할 곳이 모두 망해버렸기 때문에, 정부에 벌목 허가를 요청했다. 정부는 상근이네 집 근처의 나무 한 줄에 대한 벌목 허가를 내주었고,

toolofv.tistory.com

 

 

-> 해당 코드를 출력을 다 찍어보면 이해가 빠른데 우측 탐색이라면 해당값을 찾았을 때 l = mid+1로 이동시킨다. 이 l 은 같은 값이 없다면 고정된다. r 만 바뀌게 되어  l  r 이 되는 순간 while문이 종료된다. 이 경우라면 r l mid+1로 고정되는 순간의 mid값을 가리키고 있다. (같은 값이 존재한다면 가장 우측의 같은 값 + 1 로 l 이 고정된다. 우측으로 최대한 탐색하는 구조다.)

 

-> 하지만 이 문제에서는 c - left 의 각 값 보다 같거나 작은 right갯수를 구해서 전부 더해주는 것이기 때문에 이분 탐색의 출력은 r + 1 혹은 고정된 l 이 되어야 한다. 왜냐하면 (ㅇ, ㅇ, 2, 3, 3, 3, r , l , ㅇ ...) 일 경우 갯수는 mid 인덱스인 4가 아니라 5이기 때문.

 

 

 

import sys
input = sys.stdin.readline

n, c = map(int, input().split())
d = list(map(int, input().split()))

left = d[:n//2]
right = d[n//2:]
l_box, r_box = [], []

def sol(arr, res, idx, temp):
	if idx == len(arr):
		res.append(temp)
		return
	sol(arr, res, idx+1, temp)
	sol(arr, res, idx+1, temp+arr[idx])
	return res

def binary(arr, target):
	l, r = 0, len(arr)-1
	while True:
		if l > r:
			break
		mid = (l+r)//2
		if arr[mid] < target:
			l = mid+1
		elif arr[mid] == target:
			l = mid+1
		else:
			r = mid-1
	return l

sol(left, l_box, 0, 0)
sol(right, r_box, 0, 0)
r_box.sort()
cnt = 0

for i in l_box:
	if c-i >= 0:
		cnt += binary(r_box, c-i)
print(cnt)

 

 

반응형