본문 바로가기
알고리즘/C++

[백준] 3079 - 입국심사 by C++

by 엑츄얼리 2022. 12. 22.

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

 

3079번: 입국심사

첫째 줄에 N과 M이 주어진다. (1 ≤ N ≤ 100,000, 1 ≤ M ≤ 1,000,000,000) 다음 N개 줄에는 각 심사대에서 심사를 하는데 걸리는 시간인 Tk가 주어진다. (1 ≤ Tk ≤ 109)

www.acmicpc.net

전형적인 이분탐색 문제였다. 이분탐색을 제대로 이해하질 못해서, 이 문제를 통해 원리를 어느정도 이해할 수 있었다.

 

알고리즘 방식

이분 탐색의 대상은 상근이와 친구들이 심사를 마치는데 걸리는 시간이였다. (즉 정답)

시작값(st) = 0, 끝값(en) = Tk의 최대값 * M으로 지정해주었다.

(시작값 또한 Tk의 최소값 * M으로 지정해도 되지만, 의미 없는 것 같아 생략)

 

1. 선택된 시간(mid)에서 통과할 수 있는 사람의 최대값(sum)을 구한다.

2. 최대값이 M보다 작다면, mid 값을 키워야하므로 st = mid + 1

    최대값이 M보다 크거나 같으면, mid 값을 줄여야 하므로 en = mid - 1;

3. 구해진 en 값은 정답보다 -1작은 수이므로, (en = mid - 1인 상태로 while문을 탈출하기 때문)

    en + 1을 정답으로 출력한다.

 

 

고찰

void Solution() {
	long long st = 0, en = maxT * M;
	long long answer;
	while (st <= en) {
		long long mid = (st + en) / 2;
		long long sum = 0;
		for (int i = 0; i < N; i++) {
			sum += mid / Tk[i];
		}
		//1.sum == M 인 경우의 최솟값
		if (sum < M) {
			st = mid + 1;
		}
		else {
			answer = mid;
			en = mid - 1;
		}
		
		//2.sum == M 인 경우의 최댓값
		//if (sum > M) {
		//	en = mid - 1;
		//}
		//else {
		//  answer = mid;
		//	st = mid + 1;
		//}
	}
}

이 문제의 예제 1을 통해 이해해보겠다.

먼저 sum == M일 수 있는 값의 범위는 28 ~ 29 이다.

1. sum == M 인 경우의 최솟값

아래 두 가지 경우는 서로 독립적이다. (while 문을 탈출할 때, 아래 두가지 조건이 만족하지 않기 때문)

1. st는 반드시 28에서 멈춘다. (sum < M 을 만족하는 마지막 수는 27이고, 이 값에 +1한 28을 st에 대입하기 때문)

2. en는 반드시 27에서 멈춘다. (sum >= M을 만족하는 마지막 수는 28이고, 이 값에 -1한 27을 en에 대입하기 때문)

그렇기 때문에, 정답은 st, en + 1모두 가능하다.

 

2. sum == M 인 경우의 최댓값

아래 두 가지 경우는 서로 독립적이다. (while 문을 탈출할 때, 아래 두가지 조건이 만족하지 않기 때문)

1. en는 반드시 29에서 멈춘다. (sum > M 을 만족하는 마지막 수는 30이고, 이 값에 -1한 29을 en에 대입하기 때문)

2. st는 반드시 30에서 멈춘다. (sum <= M 을 만족하는 마지막 수는 29이고, 이 갑셍 +1한 30을 st에 대입하기 때문)

 

위 두가지 경우를 이해하면, 이분 탐색 문제에서 해당 조건을 만족하는 최댓값, 최솟값을 유연하게 선택하여 문제를 풀이할 수 있을 것 같다.

 

 

소스코드

#include <iostream>
#include <vector>

using namespace std;

int N, M;
vector<int> Tk;
long long maxT;

void Solution() {
	long long st = 0, en = maxT * M;
	while (st <= en) {
		long long mid = (st + en) / 2;
		long long sum = 0;
		for (int i = 0; i < N; i++) {
			sum += mid / Tk[i];
		}

		if (sum < M) {
			st = mid + 1;
		}
		else {
			en = mid - 1;
		}

	}
	cout << en + 1;
}

int main(void) {
	ios::sync_with_stdio(false);
	cin.tie(NULL);
	
	cin >> N >> M;

	int input;
	for (int i = 0; i < N; i++) {
		cin >> input;
		Tk.push_back(input);
		if (input > maxT)
			maxT = input;
	}

	Solution();
}

 

 

'알고리즘 > C++' 카테고리의 다른 글

[백준] 1520 - 내리막 길  (0) 2023.01.09
[백준] 18870 - 좌표 압축  (0) 2022.12.30
[백준] 2064 - IP주소  (0) 2022.12.16
[백준] 15661 - 링크와 스타  (0) 2022.12.09
[백준] 20208 - 진우의 민트초코우유 by C++  (0) 2022.11.21

댓글