본문 바로가기

알고리즘 관련/BOJ

2805 나무 자르기

이 문제를 풀기 위해서 알아야 할 기법이 있는데, 파라메트릭 서치라는 기법이다.

이분탐색에서 파생된 방법으로 수치해석의 bisection method와 유사하다.

순차적인 선택지에서 절반씩 줄여서 해를 찾는 방법으로 해가 존재하지 않더라도, 근삿값을 찾는게 핵심이다.

이 방법의 강력한 점은 입력의 크기가 크더라도 탐색 횟수는 logN이기 때문에 효과적이라는 점이다.

조금 더 수치해석적으로 보면, bisection method의 단점은 지정된 범위 내에 해가 존재하지 않는다면, 해로 수렴하는 시간이 오래걸린다.

 

프로그래밍 대회에서 배우는 알고리즘 문제해결 전략이란 책에서 이 문제에 대해 최적화 문제를 결정 문제로 바꾸기라는 파트로 나눠서 다루고 있고, 해당 구조로 바꾸는 방법도 소개하고 있다. 결정 문제라는 말이 생소하지만 결국 어떤 문제가 주어지면 결정 (Yes/No)하는 것과 같다. 또한 파라메트릭 서치의 개념도 이러한 결정 문제들에서 이분법을 사용한 빠른 탐색이 주가 된다.

 

이제 문제에 적용하여 생각해보자. 주어진 문제는 최적화 문제이기 때문에 파라메트릭 서치를 적용할 구조가 아니다.

그렇다면 어떻게 바꿔야 할까

 

이 문제에 적용시켜보면, 나무의 길이 M을 얻기 위한 높이 H를 얼마나 설정해야 하는가라는 문제를 높이를 H만큼 설정하면 나무의 길이 M을 얻을 수 있는가? 라는 문제로 바꾸어 생각해보면 된다.

 

H=1부터 생각해보자, 높이를 1로 설정하면 얻을 수 있는 나무의 길이는 M이상인가? (Y)

높이를 2로 설정하면 얻을 수 있는 나무의 길이는 M이상인가? (Y)

....

높이를 h로 설정하면 얻을 수 있는 나무의 길이는 M이상인가? (Y)

높이를 h+1로 설정하면 얻을 수 있는 나무의 길이는 M이상인가? (N)

 

이제 우리가 원하는 최댓값을 찾아보자. 나무의 길이 M이상을 최초로 얻을 수 없기 전의 높이 값이 최댓값이란걸 직관적으로 알 수 있다.

 

정답코드

더보기
#include <iostream>
#include <vector>
using namespace std;
int N, M;
bool Possible(const vector<int>& v, int height)
{
	long long sum = 0;
	for (const auto& s : v) {
		if (height >= s)
			continue;
		else
			sum += s - height;
	}
	if (sum >= M)
		return true;
	else
		return false;
}
void BinSearch(vector<int>& v)
{
	int left = 0;
	int right = 2000000000;
	int mid = (left + right) / 2;
	while (left <= right) {
		if (Possible(v, mid))
			left = mid + 1;
		else
			right = mid - 1;
		mid = (left + right) / 2;
	}
	printf("%d\n", mid);
}
int main()
{
	scanf("%d %d", &N, &M);
	vector<int> v(N, 0);
	for (int i = 0; i < N; i++)
		scanf("%d", &v[i]);
	BinSearch(v);
	return 0;
}

'알고리즘 관련 > BOJ' 카테고리의 다른 글

2225 합분해  (0) 2021.01.11
1520 내리막길  (0) 2021.01.11
7453 합이 0인 네 정수  (0) 2021.01.06
9935 문자열 폭발  (0) 2021.01.06
14503 로봇청소기  (0) 2021.01.04