본문 바로가기

Algorithm/BeakJoon

[BaekJoon 3015] 오아시스 재결합

안녕하세요. 오늘은 백준 3015번 오아시스 재결합이라는 문제를 알아보겠습니다.

 

해당 문제는

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

 

3015번: 오아시스 재결합

첫째 줄에 줄에서 기다리고 있는 사람의 수 N이 주어진다. (1 ≤ N ≤ 500,000) 둘째 줄부터 N개의 줄에는 각 사람의 키가 나노미터 단위로 주어진다. 모든 사람의 키는 231 나노미터 보다 작다. 사람

www.acmicpc.net

 

아래의 링크에서 확인하실 수 있습니다.

 

 

문제를 보시면, 시간제한이 1초이고, N의 범위가 500000입니다.

 

따라서 위 문제는 O(N^2)의 알고리즘으로 해결할 수 없으며,

기본적으로 현재를 기준으로 과거의 값을 돌아보는 형식의 로직이기 때문에 Stack을 사용하는 문제임을 추측할 수 있습니다.

 

또 한가지 주의하실 점은

 

만약에 500000개의 데이터가 모두 같은 숫자로 들어오면 어떻게 될까요?

그렇게 되면 서로 바라볼 수 있는 경우의 수는 50만 C 2가 되고, 이는 인트형의 범위를 훌쩍 넘어섭니다.

 

따라서 기본적으로 unsigned long long형 베이스나 long long형을 사용하셔서 결괏값을 표현하셔야 합니다.

 

우선 기본적으로 문제의 로직을 이해하기 위해 간단한 예시를 들어보겠습니다.

 

인풋 4 1 1 2가 들어왔다고 가정해볼게요.

 

1. 처음 4가 들어오면 앞선 사람이 아무도 없으므로 팝하고 비교할 것도 없습니다. 스택에 4를 넣어줍니다.

stack : 4

 

2. 그다음 1이 들어오면 앞선 4와 서로 마주볼 수 있습니다. 이때 1 이후에 들어오는 인풋에 대해서 4와 마주 볼 수 있는 경우의 수가 있을 수 있습니다. 따라서 현재 값보다 큰 4는 그대로 스택에 유지해줍니다.

stack : 4 1

 

3. 이제 2가 들어옵니다.

2가 1 다음으로 들어온 순간 1은 2 이후에 그 어떤숫자가 들어와도 서로 마주 볼 수 없습니다.

 

따라서

stack의 top에 위치한 원소보다 큰 원소가 새로운 인풋으로 들어오면, stack의 top은 더 이상 그 뒤의 원소와 고려할 사항이 없습니다. 따라서 이경우 1을 pop 해주면서, 1 보다 우선한 사람들과 바라볼 수 있는 경우가 몇가지인지 체크해주어야 합니다.

 

1은 4와 마주볼 수 있습니다. 따라서 stack을 pop 해주고, 결과에 1을 더해줍니다. 

4는 2보다도 큰 원소입니다. 따라서 2번 원칙과 같이 4는 그대로 스택에 유지해줍니다.

그리고 2를 스택에 push 해줍니다.

 

stack : 4 2

result : 1 {4, 1}

 

4. 같은 값인 2가 들어왔습니다.

여기서 문제가 복잡해집니다. 왜냐하면 그대로 stack에 푸시를 해주면 4 2 2가 되고, 이후에 3과 같이 2보다 큰 원소가 들어오면 앞선 2도 3과 마주할 수 있고 뒤에 있는 2도 3과 마주할 수 있습니다.

 

따라서 4 2 2에서 두개의 2가 이 문제의 결괏값을 세는데, 이후에 2보다 큰 원소에 대해서 같은 역할을 합니다. 

따라서 같은 원소가 들어오는 경우를 pair<int,int>를 사용하여 개수를 같이 포함해주겠습니다.

 

stack : {4,1} {2,2}

result : 1 {4, 1}

 

이제 3번 로직을 다시 보면

 

1은 4와 마주볼 수 있습니다. 따라서 stack을 pop 해주고, 결과에 1을 더해줍니다.

4는 2보다도 큰 원소입니다. 따라서 2번 원칙과 같이 4는 그대로 스택에 유지해줍니다.

그리고 2를 스택에 push 해줍니다.

 

여기서 stack을 pop해주고, 결과에 1을 더해주는 것이 아니라 pop 한 원소의 개수를 더해주시면 됩니다.

 

위의 로직을 바탕으로 소스코드는 다음과 같습니다.

 

#include <bits/stdc++.h>
using namespace std;

int n;
unsigned long long res;
int main(void) {
	ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
	cin >> n;
	
	stack<pair<unsigned long long, unsigned long long>> st;	
	for(int i = 0; i < n; i++) {
		unsigned long long tmp; cin >> tmp;
		pair<int,int> next = {tmp,1}; // 입력 받은 수와 갯수 1로 초기화 
		while(!st.empty()) {
			if(st.top().first <= tmp) {
				res += st.top().second;
				if(st.top().first == tmp) next.second = st.top().second+1; // 만약 동일한 원소라면 갯수 추가 
				st.pop();
			}
			else break;
		}
		if(!st.empty()) res++; // 위의 와일문을 끝냈음에도, stack이 비어있지 않다는 것은, 현재 인풋보다 큰 원소 존재 따라서 결과값 추가 
		st.push(next);
	}
	cout << res << endl;
	
	return 0;
}

 

*저의 글에 대한 피드백이나 지적은 언제나 환영합니다.

'Algorithm > BeakJoon' 카테고리의 다른 글

[BeakJoon 1285] 동전 뒤집기  (0) 2021.07.24
[BaekJoon 14319] 종이조각  (0) 2021.07.23
[BaekJoon 15684] 사다리 조작  (0) 2021.07.18
[BaekJoon 2852] NBA 농구  (0) 2021.07.10
[BaekJoon 4659] 비밀번호 발음하기  (1) 2021.07.08