[알고리즘] BOJ 1450 냅색문제

SangHun·2021년 4월 18일
0
post-custom-banner

https://www.acmicpc.net/problem/1450

처음에는 무슨 문제인지조차 이해 못했다.
내가 알던 냅색 문제랑 좀 다른 거 같기도 하고..
찬찬히 읽어보니, 무게 제한만 넘기지 않으면 각 물건들에 대해 가방에 들어가거나 안 들어가거나 두 개의 선택이 있다.

처음에는 별 생각없이 dfs로 접근했다.
모든 물건마다 넣고, 않 넣고에 따라 깊이 탐색하는 방식으로.

제출해보니 시간초과.
곰곰이 생각해보니 물건 30개면 최대 2^30 번의 연산이 필요해진다.

감이 잡히지 않아서 구글의 힘을 빌렸다.
투 포인터 문제란다. 그리고 물건을 두 개의 그룹으로 나눠야한다 카더라.
30개의 물건 조합 = 2^30
15개의 물건 조합 = 2^15. 그룹이 2개니까 이걸 두 번하면 2^16
획기적으로 줄어든다.

두 그룹으로 나눠서 연산하고 두 번째 그룹은 정렬을 했다. 그럼 무게 제한보다 커지면 바로 break 할 수 있어지니까!
나름 머리써서 짠 코드다.

num_stuff, limit = map(int, input().split())
stuff_list = sorted(list(map(int, input().split())))

answer = 0
first = []
second = []

def check_and_put(index, end, weight):
    global limit
    global num_stuff
    if end == num_stuff:
        if index == end:
            second.append(weight)
            return

        if weight <= limit:
            check_and_put(index+1, end, weight)
        else:
            return
        if stuff_list[index] + weight <= limit:
            check_and_put(index+1, end, stuff_list[index] + weight)
        else:
            return
    else:
        if index == end:
            first.append(weight)
            return
        if weight <= limit:
            check_and_put(index+1, end, weight)
        else:
            return
        if stuff_list[index] + weight <= limit:
            check_and_put(index+1, end, stuff_list[index] + weight)
        else:
            return


check_and_put(0, num_stuff // 2, 0)
check_and_put(num_stuff // 2, num_stuff, 0)

second.sort()

answer = 0
for num1 in first:
    for num2 in second:
        if num1 + num2 > limit:
            break
        answer += 1

print(answer)

보기 좋게 시간초과. 왜지?

맞다, 투 포인터!

그런데 어떻게 쓸지 감이 안 잡혔다.
음, 아무리 생각해봐도 그냥 2중 반복문밖에 안 나올텐데...

투 포인터라는 힌트를 갖고 일단 두 그룹에 모두 정렬하는 방식으로 바꿨다.

그리고 알았다!
나 스스로가 너무 멍청하다고 느껴졌다.
당연한건데 이걸 왜...

첫 번째 그룹의 포인터 i는 0부터 증가.
두 번째 그룹의 포인터 j는 뒤부터 감소.
(각 포인터가 가리키는 값의 합 <= 무게제한)이 되는 순간의 j+1값을 정답 값에 더해주면 된다.
그리고 i가 1 증가된 다음 iteration에서도 j는 이전 iteration의 자리부터 시작하면 된다...

num_stuff, limit = map(int, input().split())
stuff_list = sorted(list(map(int, input().split())))

answer = 0
first = []
second = []

def check_and_put(index, end, weight, half_list):
    global limit
    global num_stuff
    if index == end:
        half_list.append(weight)
        return

    if weight <= limit:
        check_and_put(index+1, end, weight, half_list)
    else:
        return
    if stuff_list[index] + weight <= limit:
        check_and_put(index+1, end, stuff_list[index] + weight, half_list)
    else:
        return


check_and_put(0, num_stuff // 2, 0, first)
check_and_put(num_stuff // 2, num_stuff, 0, second)

first.sort()
second.sort()

answer = 0
p2 = len(second) - 1
for num1 in first:
    while p2 > -1:
        if num1 + second[p2] > limit:
            p2 -= 1
        else:
            answer += p2 + 1
            break

print(answer)

얏호 정답!
참 초보자스러웠다...

profile
개발괴발자
post-custom-banner

0개의 댓글