2805번: 나무 자르기
문제 상근이는 나무 M미터가 필요하다. 근처에 나무를 구입할 곳이 모두 망해버렸기 때문에, 정부에 벌목 허가를 요청했다. 정부는 상근이네 집 근처의 나무 한 줄에 대한 벌목 허가를 내주었고,
www.acmicpc.net
함정이 있는 이분 탐색 문제였다.
나무를 자르는 높이가 낮을수록 얻어가는 나무는 증가한다. 즉 나무를 자르는 높이(N)과 얻어가는 나무의 양은 반비례한다.
따라서, 어떤 높이(mid)를 기준으로 나무를 잘랐을 때 얻어가는 나무의 양이 필요한 양(M)보다 작다면 mid보다 높이 자르는 경우는 고려하지 않아도 되며, 이 사실을 바탕으로 이분 탐색을 사용하는 조건을 만족하게 된다.
알고리즘은 다음과 같다.
먼저 나무들의 높이를 vector에 입력받은 다음 오름차순으로 정렬하자.
그 다음 가장 낮은 높이(lower)를 0으로 하고, 가장 높은 높이(upper)를 벡터의 마지막 원소로 하여
(lower + upper) / 2 를 mid 로 이분 탐색을 시작한다.
vector의 모든 원소에 대하여 v[i] - mid가 0보다 클 경우 sum 변수에 값을 누적 시킨다.
만약 sum이 M보다 작을 경우 mid 높이보다 같거나 높은 경우는 버려도 무방하므로 upper 값을 mid - 1로 바꿔준다.
sum이 M보다 같거나 클 경우 조건을 만족하므로 result 변수에 mid를 저장하고, 최대 높이를 찾기 위해 lower 값을 mid + 1로 바꿔준다.
(ps. 여기서 함정이 있는데 적어도 M개를 가져가야 하므로, sum이 M보다 크거나 같은 경우에 result 변수를 갱신해야한다. 작거나 같은 경우에 result 변수를 갱신하게 되면 오답이 출력된다.)
끝으로, lower이 upper보다 커질 때가지 위 작업을 반복하게 되면 result 변수에는 적어도 M미터의 나무를 얻기 위한 최대의 높이가 저장된다.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main(void)
{
ios_base::sync_with_stdio(0);
cin.tie(NULL);
int N, M;
cin >> N >> M;
vector<int> tree(N);
for (int i = 0; i < N; i++)
cin >> tree[i];
sort(tree.begin(), tree.end());
int lower = 0, upper = tree[N-1], result;
while (lower <= upper)
{
int mid = (lower + upper) / 2;
long long sum = 0;
for (int i = 0; i < N; i++)
{
if (tree[i] < mid) continue;
sum += tree[i] - mid;
}
if (sum >= M)
{
result = mid;
lower = mid + 1;
}
else
{
upper = mid - 1;
}
}
cout << result << endl;
return 0;
}
알고리즘 200일 프로젝트 - 45 day
'알고리즘 > BOJ' 카테고리의 다른 글
백준 2512번: 예산 (0) | 2020.05.20 |
---|---|
백준 1654번: 랜선 자르기 (0) | 2020.05.20 |
백준 1920번: 수 찾기 (0) | 2020.05.19 |
백준 2869번: 달팽이는 올라가고 싶다 (0) | 2020.05.19 |
백준 10816번: 숫자 카드 2 (0) | 2020.05.19 |