기타 레슨

문제

언어

  • JavaScript

순서도

  1. 이진 탐색을 진행할 범위 정하기 (left 값과 right 값 찾기)
  2. 이진 탐색을 통해서 조건을 만족하는 가장 작은 블루레이의 크기 구하기

문제 풀이 step 1

  • 기타 레슨 동영상 목록과 블루레이의 개수가 주어질 때, 블루레이의 크기의 최솟값을 구하는 문제입니다.
  • 우선, 이진 탐색을 하기 위해서는 탐색을 진행할 범위를 정해야 합니다.
    • 우리가 탐색을 통해서 얻어야 하는 정보는 블루레이의 크기이며, left 와 right 로 블루레이의 크기의 범위를 정하면 되겠습니다.
    • left 에는 가능한 블루레이의 크기의 최솟값을 구해야 합니다.
      • 이는 주어진 기타 레슨 동영상 목록 중에서 최댓값을 구하면 됩니다.
      • 최댓값을 구하는 이유는 그렇게 해야 모든 레슨 동영상을 블루레이로 만들 수 있기 때문입니다.
    • right 에는 가능한 블루레이의 크기의 최댓값을 구해야 합니다.
      • 이는 모든 기타 레슨 동영상 목록의 값들의 합을 구하면 됩니다.
  • 범위를 정했으니, 이진 탐색을 진행하면 됩니다.
    • 매 탐색마다 나오는 mid 값으로 블루레이를 만들어보고, 만든 블루레이의 개수를 셉니다.
    • 블루레이의 개수가 m 보다 크다면, 너무 조금씩 블루레이로 만든 것이기 때문에 left 값을 mid + 1 로 갱신합니다.
    • 블루레이의 개수가 m 보다 같거나 작다면, 너무 많이씩 블루레이로 만든 것이기 때문에 right 값을 mid - 1 로 갱신합니다. (그리고 min 값을 갱신합니다.)
  • 위 과정을 통해서 찾아낸 min 값을 출력하면 정답입니다.
  • 추가 설명은 주석으로 작성하겠습니다.

후기

  • 오랜만에 이진 탐색 문제를 풀어서, 블로그의 예전의 풀이를 쭉 훑어보고 풀었습니다.
  • 다시 한 번 확인할 수 있었던 것은,
    • 이진 탐색 문제에서는 탐색 범위를 정확하게 정해야 한다는 점과
    • 이진 탐색은 특정 값을 찾는 상황뿐만 아니라, 가장 조건을 만족하는 최적의 값을 찾는 상황에서도 쓰인다는 점입니다.

소스 코드

const input = require("fs")
	.readFileSync("/dev/stdin")
	.toString()
	.trim()
	.split("\n");

// mid 값으로 블루레이를 만들어보고, 만든 블루레이의 개수를 구하는 함수
const getCnt = (n, lessons, mid) => {
	let cnt = 0;
	let sum = 0;
	for (let i = 0; i < n; i++) {
		sum += lessons[i];

		if (sum > mid) {
			cnt += 1;
			sum = lessons[i];
		}
	}

	return cnt + 1;
};

const solution = (input) => {
	const [n, m] = input[0].split(" ").map(Number);
	const lessons = input[1].split(" ").map(Number);

	let min = 1;
	// 이진 탐색의 범위 정하기
	let [left, right] = [0, 0];
	for (let i = 0; i < n; i++) {
		left = Math.max(left, lessons[i]);
		right += lessons[i];
	}

	// 이진 탐색
	while (left <= right) {
		let mid = parseInt((left + right) / 2);

		const cnt = getCnt(n, lessons, mid);

		// 블루레이의 개수가 m 보다 큰 경우
		if (m < cnt) {
			left = mid + 1;
		}
		// 블루레이의 개수가 m 보다 같거나 작은 경우
		else {
			min = mid;
			right = mid - 1;
		}
	}

	// 가장 최적의 값 return
	return min;
};

console.log(solution(input));