사용자 도구

사이트 도구


ps:problems:boj:31412

군수품 창고 정리

ps
링크acmicpc.net/…
출처BOJ
문제 번호31412
문제명군수품 창고 정리
레벨플래티넘 5
분류

이분탐색

시간복잡도O(m!*m*logn*log(a*n))
인풋사이즈n<=500,000, m<=7, a<=1,000,000
사용한 언어Python 3.11
제출기록93984KB / 668ms
최고기록668ms
해결날짜2024/02/20
출처

제3회 보라매컵 본선 Open Contest - G

풀이

  • 이 문제처럼 뭔가 아이템들을 고르게(최댓값을 최소로) 분배하는 방법을 찾는 유형은 이분탐색으로 풀리는 경우가 매우 많다.
  • 그 이유는, 최댓값을 제한한채로 아이템을 나누것이 가능한지 아닌지를 확인하는것이 보통 간단하게 되기 때문이다. 최대한 최댓값에 가깝게 분배하도록 시뮬레이션을 해보는 방식으로 확인해보면 된다.
  • 이 문제도 마찬가지로, 이분탐색을 사용한다. 아이템을 각 분대에 {분대의 병사수 * 최댓값} 이하의 범위에서 최대한 많이 나눠줄수 있도록 묶어서 나눠줘보고, 그래서 모든 아이템을 다 나눠줄수 있는지 확인하면 된다.
  • 하지만 신경쓸 점이 몇가지 있다
  • 한가지는 아이템들을 앞에서부터 묶어서 분대 개수만큼의 묶음을 만들었을때, 각각을 분대들에 매핑시키는 방법을 또 고려해야 한다. 다행히도 분대 개수 M이 최대 7개로 작기때문에, 그냥 무식하게 M!의 모든 순서에 대해서 아이템들을 나누는게 가능한지 확인해서, 한개의 순서라도 가능한 순서가 있다면 가능한것으로 처리하는 것이다.
  • 두번째는, 분대들의 순서와 최댓값까지가 정해졌을때, 아이템을 나눠주는 시뮬레이션을 O(n)에 처리하는 것은 너무 느리다는 것이다. 이렇게 하면 결정문제 한번을 푸는데에 O(n*m!)이 되는데 이것은 너무 크다. 아이템을 나눠줄때 {분대의 병사수 * 최댓값} 이하의 범위에서 최대한 많이 나누는 값을 찾는 것에 다시 이진탐색을 사용하는 방법이 있다. 먼저 아이템들의 누적합 배열을 만들어놓고, 여기에 이진탐색을 M번 적용하는 식으로 처리한다면, 나눠주는 시뮬레이션을 O(m*logn)에 처리할수 있고, 모든 순서에 대해서 처리하면 O(m!*m*logn)이 된다.
  • 답의 범위를 1~{아이템의 총합} 으로 놓으면, 결정 문제를 O(log(a*n))번 풀어야 한다 (a=박스 한개에 들어갈수 있는 아이템의 최대개수). 총 시간복잡도는 O(m!*m*logn*log(a*n))이 된다

코드

"""Solution code for "BOJ 31412. 군수품 창고 정리".

- Problem link: https://www.acmicpc.net/problem/31412
- Solution link: http://www.teferi.net/ps/problems/boj/31412

Tags: [binary search]
"""

import bisect
import functools
import itertools
from teflib import binsearch


def is_vaild(limit, psum, b):
    for b_perm in itertools.permutations(b):
        prev = 0
        for b_i in b_perm:
            pos = bisect.bisect_right(psum, prev + b_i * limit)
            if pos == len(psum):
                return True
            prev = psum[pos - 1]
    return False


def main():
    N, M = [int(x) for x in input().split()]  # pylint: disable=unused-variable
    a = [int(x) for x in input().split()]
    b = [int(x) for x in input().split()]

    psum = [v := 0] + [v := v + x for x in a]
    is_valid_func = functools.partial(is_vaild, psum=psum, b=b)
    answer = binsearch.minimum_valid_integer(1, sum(a) + 1, is_valid_func)

    print(answer)


if __name__ == '__main__':
    main()

토론

댓글을 입력하세요:
W R​ W K N
 
ps/problems/boj/31412.txt · 마지막으로 수정됨: 2024/03/05 15:02 저자 teferi