문제 : https://programmers.co.kr/learn/courses/30/lessons/43238

 

서론

혼자 풀어내지 못한 문제.

분류가 이분탐색인데 뭘 이분탐색하라는건지 이해를 못했었다.

그리고 이분의 블로그를 참고하여 이해할 수 있었다.

https://antananarivo.tistory.com/141

원하는 답이 시간이니 시간을 이분탐색 해야한다고 생각할 수도 있었을것 같은데 왜 못했을까 싶다. 시간은 1부터 흘러가야한다는 굳어진 생각을 했었던건 아닌가 싶다.

 


풀이

시간을 기준으로 이분탐색하여 적합한 시간을 찾아야 한다.

최소시간은 1, times.min, n/times.length 등 다양하게 만들 수 있는데

여기서는 n*times.min/times.length 으로 구현했다. 

최소시간 중 가장 커 보이는 값으로, 모든 심사관의 작업 속도가 가장 빠른 심사관과 동일하고 비어있는 심사대가 보이면 곧바로 심사받는 경우의 시간이다.

최대시간은 모든 사람이 가장 느린 심사관 1명에게만 줄서서 심사를 받는 경우로, n * times.max다.

주의할점은 최소시간과 최대시간의 자료형변환이다. 자료형이 int일 경우 제대로된 출력값이 안나온다.

 

그리고 이분탐색하면서 특정 시간에서 n과 같은 결과가 나오는지 확인한다.

예를들어 예제의 정답인 28분은

1번 심사관이 4명을 처리할 수 있고 ( 28 / 7 = 4 )

2번 심사관이 2명을 처리할 수 있다. ( 28 / 10 = 2 )

즉 28분은 6명을 처리할 수 있으므로 정답이다 ( 6 + 2 )

 

특정 시간에서 정답보다 많은 인원을 처리할 수 있으면 최대시간을 조정해주고 정답만큼 처리할 수 없으면 최소시간을 조절한다.

 

그리고 예제1번은

시간이 26에서 5명, 27에서 5명, 28에서 6명, 29에서 6명, 30에서 7명을 처리할 수 있다.

즉 6명을 처리할 수 있는 시간이 28, 29 두가지다.

그렇기에 이분탐색에서 n명이 나왔다고 바로 결과를 출력해선 안되며 n명이 나오는 시간의 최소값을 출력해야한다.

while (start <= end) {
	long long num = 0;
	mid = (start + end) / 2;
	for (int i = 0; i < timesNum; i++) { 
		num += mid / times[i]; // 현재시간만큼 각 심사관이 처리할 수 있는 모든 인원수를 더한다
	}
	if (num < n) start = mid + 1;
	else {
		end = mid - 1;
		answer = mid; // n을 만들 수 있는 현재시간 중 최소값을 출력하기 위해 현재시간이 n과 같거나 클때만 결과를 갱신한다.
	}
}

 


디버깅

케이스 8에서 실패가 출력되었다.

원인은 자료형으로,

long long start = (n * times.front()) / timesNum

위 대입연산자의 오른쪽 각 항들이 모두 int형으로 선언되어있었고 계산 결과는 int를 초과했다.

오른쪽 항 중 하나를 long long으로 변경하여 계산결과가 묵시적 형변환이 되도록했다.

 

수정 전

int timesNum = times.size();

수정 후

long long timesNum = times.size();

 


구현

// c++

#include <string>
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;

long long solution(int n, vector<int> times) {
	long long answer = 0;

	long long timesNum = times.size();
	sort(times.begin(), times.end());

	long long start = (n * times.front()) / timesNum;
	long long end = n * times.back();
	long long mid = 0;
	while (start <= end) {
		long long num = 0;
		mid = (start + end) / 2;
		for (int i = 0; i < timesNum; i++) {
			num += mid / times[i];
		}
		if (num < n) start = mid + 1;
		else {
			end = mid - 1;
			answer = mid;
		}
		//cout<<num<<" "<<mid<<endl;
	}

	return answer;
}