포스트

[Math] 바이러스 검사 - 삼성전자

바이러스 검사를 식당마다 올림 나눗셈으로 계산해 푼 기록입니다. 팀장 한 명이 강제라는 조건을 식에 넣는 자리와 답이 int를 넘는 이유를 정리합니다.

[Math] 바이러스 검사 - 삼성전자

문제

바이러스 검사

바이러스의 확산을 막기 위해 총 n개의 식당에 있는 고객들의 체온을 측정하고자 합니다. 체온을 측정하는 검사자는 검사팀장과 검사팀원으로 나뉘어집니다. 팀장과 팀원이 검사할 수 있는 고객의 수가 다르며, 한 가게당 팀장은 오직 한 명, 팀원은 여러명 있을 수 있습니다. 하지만 가게당 팀장 한 명은 무조건 필요합니다. 가게에 검사팀원만 존재하는 경우는 있을 수 없습니다. 팀장이든 팀원이든 담당한 가게에 대해서만 검사합니다.

n개의 식당 고객들의 체온을 측정하기 위해 필요한 검사자 수의 최솟값을 구하는 프로그램을 작성해주세요.

입력

첫째 줄에는 식당의 수를 의미하는 정수 n이 주어집니다.

둘째 줄에는 각 식당에 있는 고객의 수가 공백을 사이에 두고 주어집니다.

셋째줄에는 검사팀장이 검사할 수 있는 최대 고객 수와 검사팀원이 검사할 수 있는 최대 고객 수가 공백을 사이에 두고 주어집니다.

제한 조건

  • 1 ≤ n ≤ 1,000,000
  • 1 ≤ 각 식당에 있는 고객의 수 ≤ 1,000,000
  • 1 ≤ 팀장 혹은 팀원 한 명이 검사 가능한 최대 고객의 수 ≤ 1,000,000
  • 모든 고객 수는 정수입니다.

출력

n개의 식당의 고객들을 모두 검사하기 위한 검사자의 최소의 수를 출력하세요

입력 예제

예제 1

1
2
3
1
1
2 2
1
1

예제 설명입니다. 밑의 그림에서 REST은 식당, CUST는 고객, LDR은 검사 팀장, MBR은 검사 팀원 입니다.

하나의 식당에 한 명의 손님이 있고, 팀장 한 명이 검사를 진행하면 되므로, 총 필요한 검사자는 1명 입니다.

식당 한 곳에 고객 한 명, 팀장 한 명이 검사하는 그림

예제 2

1
2
3
5
999999 999999 999999 999999 999999
111111 5
1
888895

예제 설명입니다. 5개의 식당에 각 999999명의 손님이 있습니다. 각 식당마다, 팀장 한 명이 111111명을 검사하고, 팀원들이 한 명당 5명씩 총 888888명의 손님을 검사해야 합니다. 팀원 177777명이 5명씩 검사하고, 1명의 팀원이 3명만 검사하면 되므로, 필요한 팀원은 177777 + 1 = 177778명 입니다. 그러므로, 하나의 식당에 필요한 총 검사자는 1 + 177778 = 177779명 이고, 5개의 식당에 필요한 검사자는 177779 x 5 = 888895명 입니다.

식당 다섯 곳에 각각 팀장 한 명과 팀원 177778명이 배치된 그림

예제 3

1
2
3
3
10 15 13
7 14
1
6

예제 설명입니다. 3개의 식당에 각 10명, 15명, 13명의 손님이 있습니다. 각 식당에서, 팀장이 7명의 사람을 검사하고, 남은 사람들은 한 명의 팀원이 검사할 수 있으므로, 하나의 식당에는 2명의 검사자가 필요합니다. 그러므로 필요한 총 검사자는 2 x 3 = 6명 입니다.

식당 세 곳에 각각 팀장 한 명과 팀원 한 명이 배치된 그림

힌트

답의 범위가 10^12 까지 커질 수 있는 문제입니다. C/C++/Java에서의 int형의 경우 2^31 - 1 까지만 값 표현이 가능하므로 언어별로 다른 자료형을 사용하셔야 합니다.

(1) C

int대신 long long 타입을, 포맷에서는 %d 대신 %lld를 사용해주세요.

(2) C++

int대신 long long 타입을 사용해주세요.

(3) Java

int대신 long 타입을 사용해주세요.

제한

  • Time Limit: 1000 ms
  • Memory Limit: 220 MiB

발상

여기는 직접 적습니다. 아래 셋을 채웁니다.

  1. 문제를 무엇으로 바꿔 읽었는지
  2. 먼저 떠오른 방법과 그것이 왜 안 되는지. 입력 크기를 근거로
  3. 그래서 무엇을 골랐고 왜 그것이면 되는지

풀이

제출한 코드입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
N = int(input())
guest = list(map(int,input().split()))
leader_max, member_min = list(map(int,input().split()))


for i in range(N):
    guest[i] -= leader_max

answer = 0
for i in range(N):
    if ( guest[i] > 0 ):
        answer += (guest[i] + member_min-1) //member_min

print(answer+N)

팀장 한 명이 무조건 필요하다는 조건이 두 자리에 나뉘어 들어가 있습니다. 첫 번째 반복에서 guest[i] -= leader_max 로 팀장이 맡을 몫을 미리 빼 두고, 마지막 줄의 answer + N 에서 식당 수 N을 그대로 더합니다. 식당마다 팀장이 정확히 한 명이므로 팀장 수의 합은 N입니다.

guest[i] 가 0 이하이면 팀장 혼자로 끝나므로 팀원을 세지 않습니다. 0보다 크면 남은 인원을 팀원 한 명당 member_min 명씩 나눠 맡습니다. (guest[i] + member_min - 1) // member_min 이 올림 나눗셈입니다. 마지막 팀원이 정원을 다 채우지 못해도 한 명은 필요하기 때문에 내림이 아니라 올림입니다.

예제 세 개를 넣은 결과입니다.

1
2
3
1
1
2 2
1
1
1
2
3
5
999999 999999 999999 999999 999999
111111 5
1
888895
1
2
3
3
10 15 13
7 14
1
6

복잡도

구분
입력 크기1 ≤ n ≤ 1,000,000, 고객 수와 검사 가능 인원 각각 1 이상 1,000,000 이하
시간O(n)
공간O(n)

시간이 O(n)인 이유는 식당을 두 번 훑고 각 식당에서 하는 일이 뺄셈 한 번과 나눗셈 한 번으로 고정이기 때문입니다. 식당끼리 영향을 주고받지 않아서 정렬이나 탐색이 필요 없습니다.

공간이 O(n)인 이유는 둘째 줄을 통째로 리스트에 담기 때문입니다. 한 줄에 최대 100만 개의 정수가 들어옵니다. 값을 읽으면서 바로 더하면 O(1)로 줄일 수 있지만 제출한 코드는 리스트를 만듭니다.

이 복잡도로 제한 안에 드는 근거입니다. n = 1,000,000에 고객 수를 1부터 1,000,000 사이에서 무작위로 채우고 팀장과 팀원의 정원을 각각 1로 둔 입력을 만들어 돌려 보니 파이썬 3.14에서 0.35초가 걸렸습니다. 시간 제한 1000ms 안에 듭니다. 채점 결과는 319ms였습니다.

답의 크기도 봅니다. 식당 100만 개에 각각 고객이 100만 명이고 팀장과 팀원이 한 명씩만 맡을 수 있으면 검사자는 10^12명입니다. 파이썬은 정수 크기에 제한이 없어서 그냥 넘어가지만, 문제의 힌트가 짚은 대로 C나 자바로 풀면 이 자리에서 int가 넘칩니다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.