본문 바로가기
알고리즘/백준

[백준 3020] 개똥벌레 (C++)

by fortissimo 2024. 10. 17.

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

 

문제


개똥벌레 한 마리가 장애물(석순과 종유석)로 가득찬 동굴에 들어갔다. 동굴의 길이는 N미터이고, 높이는 H미터이다. (N은 짝수) 첫 번째 장애물은 항상 석순이고, 그 다음에는 종유석과 석순이 번갈아가면서 등장한다.

아래 그림은 길이가 14미터이고 높이가 5미터인 동굴이다. (예제 그림)

 

이 개똥벌레는 장애물을 피하지 않는다. 자신이 지나갈 구간을 정한 다음 일직선으로 지나가면서 만나는 모든 장애물을 파괴한다.

위의 그림에서 4번째 구간으로 개똥벌레가 날아간다면 파괴해야하는 장애물의 수는 총 여덟개이다. (4번째 구간은 길이가 3인 석순과 길이가 4인 석순의 중간지점을 말한다)

 

하지만, 첫 번째 구간이나 다섯 번째 구간으로 날아간다면 개똥벌레는 장애물 일곱개만 파괴하면 된다.

동굴의 크기와 높이, 모든 장애물의 크기가 주어진다. 이때, 개똥벌레가 파괴해야하는 장애물의 최솟값과 그러한 구간이 총 몇 개 있는지 구하는 프로그램을 작성하시오.

 

입력


첫째 줄에 N과 H가 주어진다. N은 항상 짝수이다. (2 ≤ N ≤ 200,000, 2 ≤ H ≤ 500,000)

다음 N개 줄에는 장애물의 크기가 순서대로 주어진다. 장애물의 크기는 H보다 작은 양수이다.

 

출력


첫째 줄에 개똥벌레가 파괴해야 하는 장애물의 최솟값과 그러한 구간의 수를 공백으로 구분하여 출력한다.

 

문제 풀이


이분 탐색 문제.

 

만약 1번 구간을 선택해서 날아간다고 가정해보자. 크기가 1이상인 석순은 모두 파괴해야 한다. 2번 구간을 선택해서 날아간다고 가정하면, 크기가 2 이상인 석순은 모두 파괴해야 한다. 즉, i번 구간을 선택했을 때 순서와 상관없이 크기가 i 이상인 석순을 모두 파괴해야 한다. 따라서 찾는 값보다 같거나 큰 값이 처음으로 나오는 위치를 얻어오는 lower_bound()를 사용하면 석순을 파괴해야 하는 개수를 알 수 있다.

만약 1번 구간을 선택해서 날아간다고 하면, 크기가 H인 종유석은 모두 파괴해야 한다. 2번 구간을 선택해서 날아간다고 하면, 크기가 H-1 이상인 종유석은 모두 파괴해야 한다. 즉, i번 구간을 선택했을 때 크기가 H-i 이상인 종유석을 모두 파괴해야 하므로, 석순을 파괴해야 하는 개수와 같이 lower_bound()를 사용해서 종유석을 파괴해야 하는 개수를 알 수 있다.

 

lower_bound()는 처음으로 나오는 위치를 찾아주는 것이므로, 파괴해야 하는 장애물은 lower_bound()로부터 얻어진 인덱스부터 석순 혹은 조유석의 크기만을 모아둔 벡터 혹은 배열의 마지막까지이다. 반복문으로 1번 구간부터 H번 구간까지 반복해서 lower_bound()를 이용해 파괴해야 하는 장애물의 개수를 구해주면 된다.

 

아래는 코드.

더보기
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

int main()
{
	cin.tie(NULL);
	ios::sync_with_stdio(false);

	int N, H, height;
	cin >> N >> H;
	vector<int> ceiling;
	vector<int> bottom;
	long long answer = 100000000001;
	long long count = 0;
	for (int i = 0; i < N; i++)
	{
		cin >> height;
		if (i % 2 == 0)
		{
			bottom.push_back(height);
		}
		else
		{
			ceiling.push_back(H - height);
		}
	}
	sort(bottom.begin(), bottom.end());
	sort(ceiling.begin(), ceiling.end());
	for (int i = 1; i <= H; i++)
	{
		long long bottomIndex = lower_bound(bottom.begin(), bottom.end(), i) - bottom.begin();
		long long ceilingIndex = lower_bound(ceiling.begin(), ceiling.end(), i) - ceiling.begin();
		long long currentBreak = bottom.size() - bottomIndex + ceilingIndex;
		if (currentBreak < answer)
		{
			answer = currentBreak;
			count = 1;
		}
		else if (currentBreak == answer)
		{
			count++;
		}
	}
	cout << answer << " " << count << "\n";
	return 0;
}

'알고리즘 > 백준' 카테고리의 다른 글

[백준 1516] 게임 개발 (C++)  (0) 2024.10.19
[백준 14562] 태권왕 (C++)  (0) 2024.10.18
[백준 17479] 정식당 (C++)  (0) 2024.10.16
[백준 2567] 색종이 - 2 (C++)  (0) 2024.10.15
[백준 2331] 반복수열 (C++)  (0) 2024.10.14