Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

4-oesnuj #14

Merged
merged 2 commits into from
May 8, 2024
Merged

4-oesnuj #14

merged 2 commits into from
May 8, 2024

Conversation

oesnuj
Copy link
Member

@oesnuj oesnuj commented Apr 6, 2024

🔗 문제 링크

백준 | 옥상 정원 꾸미기

✔️ 소요된 시간

2시간

✨ 수도 코드

교황님이 스택 문제로 을 추천해주셨는데 저번에 풀어본 문제라서 비슷한 문제라고 나와있는 이 문제를 도전했습니다.

먼저 떠오르는 풀이는 각 빌딩의 오른쪽에 자신보다 높은 빌딩 전까지의 원소의 갯수를 다 더하면 답이었습니다.
그런데 이 방법은 최악의 경우O(n제곱)정도의 시간 복잡도를 가져 다른 풀이를 생각해봤습니다.

스택 문제였기에 스택으로 어떻게 풀어야할지 1시간 넘게 고민했네요.
빌딩의 높이를 왼쪽부터 오른쪽으로 하나씩 입력받고 받을때 마다 아래 로직대로 처리를 해줍니다.
흐름대로 설명을 해보자면

Note

  1. 먼저 스택이 비어있는 상태니까 첫번째 빌딩의 높이를 스택에 넣는다.
  2. 두번째 입력부터 아래 로직을 거치는데
  • 스택에 들어있는 빌딩 높이들 중에 입력받은 빌딩의 높이보다 낮은 빌딩은 다 뺍니다.
  • 낮은 빌딩을 다 뺐다면 스택에는 입렵받은 빌딩보다 높거나 아니면 모두 다 빠져서 비어있을 겁니다.
  • 이때 스택의 남은 빌딩 수가 곧 입력받은 빌딩을 볼 수 있는 빌딩이므로 count에 저장합니다.
    3.위 3단계가 끝나면 스택에 입력받은 빌딩을 스택에 넣어줍니다.

🚩예시

건물 5개이고 높이 {10, 5, 1 ,3 ,14}가 순차적으로 입력됩니다.

  1. 현재 스택이 비어있으니 현재 입력 push 합니다.
    Stack
    [ 10 ]

  2. 두번째 입력
    5보다 낮은 빌딩은 스택에 없다. 스택에 남아있는 빌딩 수가 곧 5를 볼 수 있는 빌딩수(count에 1 더한다.)
    나를 볼 수 있는 빌딩수 처리가 끝났으니 push한다.
    Stack
    [ 10 5 ]

  3. 세번째 입력
    1보다 낮은 빌딩은 스택에 없다. 스택에 남아있는 빌딩 수가 곧 1를 볼 수 있는 빌딩수 (count에 2 더한다.)
    나를 볼 수 있는 빌딩수 처리가 끝났으니 push한다.
    Stack
    [ 10 5 1 ]

  4. 네번째 입력
    3보다 낮은 빌딩은 스택에 있다. 3보다 낮은 빌딩을 다 pop 시킨다.
    Stack
    [ 10 5 ] <- 1이 pop, 더 이상 3보다 낮은 빌딩은 없다. 스택에 남아있는 빌딩수가 곧 3을 볼 수 있는 빌딩수 (count 2에 2 더한다.)
    나를 볼 수 있는 빌딩수 처리가 끝났으니 push한다.
    Stack
    [ 10 5 3 ]

  5. 다섯번째 입력
    14보다 낮은 빌딩이 스택에 있다. 14보다 낮은 빌딩을 다 pop 시킨다.
    Stack
    [ ] <- 3, 5, 10 전부 pop 되어버림. 스택에 남아있는 빌딩수가 없으니 14를 아무도 보지 못한다.
    스택이 비었어버렸으니 push한다.
    Stack
    [ 14 ]

---------------------------------------모든 입력이 처리되었다. 이때 count가 정답이된다.

⚠주의할 점

이 문제의 입력을 보면 빌딩의 개수 N개가 (1 <= N <= 80,000)입니다.
예를들어 첫번째 빌딩의 높이가 80001, 두번째가 80000, .... N번째가 1이면 볼수 있는 건물의 개수는
80000 + 79999+ 79998 + .... + 1 입니다.
이를 식으로 나타내면 1부터 N까지의 합으로 n(n+1)/2로 n^2입니다.
입력의 최대값인 80000^2는 6,400,000,000이므로 int형이 나타낼 수 있는 범위를 초과합니다.
즉 count 변수를 int 형으로 선언하면 안되고 long long형으로 선언해야 합니다.

📂소스코드

#include <iostream>
#include <vector>

using namespace std;

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

	int n;
	cin >> n; //빌딩 개수 입력

	vector <int> v(n);  //빌딩 높이를 받을 벡터 선언
	vector <int> s;     //빌딩 높이들을 적절하게 넣을 스택 선언
	for (auto& i : v)
		cin >> i;   //각 빌딩 높이 입력

	long long int count = 0;  //count가 int형을 넘어서기에 long long int type으로 둠
	for (const auto& height : v) // 모든 빌딩 높이를 하나씩 순회한다.
	{
		while (true)
		{
			if (s.empty()) //스택이 비었다면 현재 빌딩 높이 스택에 push
			{
				s.push_back(height);
				break;
			}
			if (height < s.back()) //현재 빌딩 높이가 스택의 top보다 높은 빌딩이라면(볼 수 있는 빌딩이라면)
			{
				count += s.size();
				//스택에는 현재 빌딩보다 높은 빌딩 밖에 없으니 스택의 크기 = 현재 빌딩을 볼 수 있는 빌딩 수
				s.push_back(height);
				break;
			}
			s.pop_back(); //현재 빌딩이 스택의 top보다 높다면(어짜피 나를 볼 수 없는 빌딩이니) 전부 pop
			//스택이 비거나 현재 빌딩보다 높은 top이 나올때까지 pop한다.
			// 언제까지 pop 할까?
			//case 1. 스택이 비면 나를 볼 수 있는 빌딩은 없다는 뜻이니 스택에 push
			//case 2. 나보다 높은 빌딩이 나온다면 그때 스택의 크기 = 나를 볼 수 있는 빌딩 수 를 count에 추가
		}
	}
	cout << count;
	return 0;
}

📚 새롭게 알게된 내용

문제를 풀때는 내림차순으로 스택의 원소를 구성하면서 로직을 만들자고 푼게 아니고 이것저것 다 생각보고 해보다가 풀고보니 "어 내림차순처럼 스택이 구성되네?" 였습니다.
다 풀고나서 찾아보니 모노톤 스택이라는 것도 있더라구요.
monotone : 단조로운
모노톤 스택은 스택의 원소를 중복을 허용하지 않고 오름차순, 또는 내림차순으로 유지시키면서 값을 저장시키는 것
새 항목이 push 될때 단조를 유지하면서 저장한다. push전에 단조를 유지할 수 있도룩 pop을 수행한다.
이때 정렬되는 과정에서 발생하는 정보들이 유용하다.
이 방식을 이용하면 아까 처음 생각한 풀이와 달리 시간 복잡도가 O(n)인 것을 알 수 있었습니다.
참고한 사이트

처음은 어찌저찌 구현했지만 스택을 이렇게 활용할 수 있다는 점을 알고 다른 비슷한 유형을 풀어보니 쉽게 풀렸습니다.

for (auto& i : v)
cin >> i; //각 빌딩 높이 입력

long long int count = 0; //count가 int형을 넘어서기에 long long int type으로 둠
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

long long int type은 처음보는 타입인데 준서님 덕분에 새롭게 알아갑니다!

@Ghdrn1399
Copy link
Contributor

Ghdrn1399 commented Apr 15, 2024

모노톤 스택이라는 개념도 있었군요!!.. 처음 알아갑니다ㅎㅎ 밑에는 모노톤 스택 예시코드인데 참고 하시면 좋을것 같아서 가져와봤어요!!

import java.util.Scanner;
import java.util.Stack;


public class baek_6198 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        Stack<Integer> arr = new Stack<>();
        int[] dp = new int[n];
        long answer = 0;
        for(int i=0; i<n; i++){
            dp[i] = sc.nextInt();
        }
        for(int i=0; i<n; i++){
            int data = dp[i];
            while(arr.size()!=0 && arr.peek()<=data) arr.pop();
            answer+=arr.size();
            System.out.println(arr.size());
            arr.add(data);
        }
        System.out.println(answer);
    }
}

Copy link
Collaborator

@pu2rile pu2rile left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

우선 문제 풀이 정말 잘 봤습니다! 덕분에 시간 초과 해결 방법과 모노톤 스택이라는 것도 알게 되었어요

다만, 제가 c++를 잘 알지 못한 나머지 ios_base::sync_with_stdio(false)라는 구문을 처음 봐서 찾아보니 ios_base::sync_with_stdioc의 stdio와 cpp의 iostream을 동기화시켜주는 역할을 하고, ios_base::sync_with_stdio(false)동기화를 비활성화시켜줌으로써 C++만의 독립적인 버퍼가 생성되어 c의 버퍼와 병행하여 사용할 수 없게 되지만, 사용하는 버퍼의 수가 줄어들었기 때문에 실행 속도는 빨라지게 된다고 하더라고요!

덕분에 신기한 것 많이 알아가요 이번 pr 수고하셨습니다~

Copy link
Collaborator

@suhyun113 suhyun113 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아직 알고리즘 문제에 익숙하지 않아 다른 문제 유형들을 볼 때마다 이해하기 힘들었는데, 스택문제가 나와서 좋았어요!! 저도 스택문제를 풀었는데, 준서님 문제는 제가 풀었던 것보다 난이도가 훨씬 있더라구요..그래서 이해하기 조금 힘들었는데, 스택을 알아서 그나마 이해할 수 있었던 것 같아요!! 저도

ios_base::sync_with_stdio(false);
	cin.tie(NULL);

이런 코드는 처음봐서 어떤 경우 사용하는지 궁금해서 찾아봤어요! 백준에서의 시간초과를 해결하기 위해 사용하는 코드더라구요. 이 코드를 통해 입출력 속도가 빨라지고, 그로 인해 시간초과로 틀렸던 문제가 맞게 된다니...저도 c++을 이용할 때 사용해봐야 겠어요!
처음에 문제를 이해하는게 어려웠는데, 예시를 들어서 알려주셔서 이해가 되더라구요. 저도 준서님 코드 참고해서 한번 풀어봐야 겠네요!
PR수고하셨어요👻

@oesnuj oesnuj merged commit f899cda into main May 8, 2024
1 check passed
@oesnuj oesnuj deleted the 4-oesnuj branch May 8, 2024 12:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants