관리 메뉴

Mini

[알고리즘] 백준 6198 옥상정원꾸미기 // 스택, 역발상, 자기중심적 사고, st.size() 도 의미가 있다. 본문

Algorithm/스택

[알고리즘] 백준 6198 옥상정원꾸미기 // 스택, 역발상, 자기중심적 사고, st.size() 도 의미가 있다.

Mini_96 2024. 12. 29. 01:23

* 풀이

  • 유사문제들의 풀이의 공통점
    • 계산이 끝난 원소를 바로 while문으로 pop 하는 특징
    • (top <= current) -> 나보다 작은 top -> 나를 볼수없음 -> pop 
    • 남은 스택의 원소갯수 = 나를 볼수있는 빌딩의 갯수!
  • 예시 낚시
    • 예시는 관리인 시점에서 탐색중인 current가 아니라 탐색이 끝난 element 관점으로 서술되어있다.
    • 이러면, 상태관리가 어려워진다.
    • 문제 해결에서 "현재 위치(current)"를 중심으로 생각하는 것은 매우 좋은 접근 방법

      이런 관점이 유용한 이유는:

      1. 문제를 더 작은 단위로 나눌 수 있음
         - "전체 빌딩이 몇 개를 볼 수 있는가?" 대신
         - "현재 빌딩(나)에 대해서는 어떤 일이 일어나는가?"

      2. 상태 관리가 쉬워짐
         - 현재 시점에서 필요한 정보만 유지
         - 이 문제에서는 스택에 현재보다 높은 빌딩만 남김

      3. 반복되는 패턴을 찾기 쉬움
         - 각 위치에서 동일한 로직 적용
         - 이 문제에서는 "나보다 작은 빌딩 제거" -> "스택 크기 확인" -> "나를 스택에 추가"

      비슷한 예시들:
      ```
      - 투 포인터: 현재 위치의 포인터를 기준으로 판단
      - DFS/BFS: 현재 노드에서 할 수 있는 행동 결정
      - DP: 현재 상태에서의 최적해 계산
      ```

      이렇게 "현재"를 중심으로 생각하면 복잡한 문제도 단순한 단위로 나누어 해결가능

나를 볼수있는 빌딩이 몇개인지 세는 문제로 바꿔야한다.

* 전체코드

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

ll n;
vector<ll> arr;
stack<pair<ll,ll>> s;
ll ret;

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

    cin >> n ;

    for (int i = 0; i < n; ++i) {
        ll tmp;
        cin >> tmp;
        arr.push_back(tmp);
    }

    s.push({0,arr[0]});
    for (int i = 1; i < n; ++i) {
        // 현재 빌딩보다 작거나 같은 빌딩은 나를 볼수 없으므로 제거
        while(s.size() && s.top().second <= arr[i]) {
            s.pop();
        }
        // 스택에 남아있는 빌딩의 수가 현재 빌딩을 볼 수 있는 빌딩의 수
        // 나보다 작은 빌딩은 위에서 다 제거했음.
        ret+=s.size();

        // 현재 빌딩 정보를 스택에 추가
        s.push({i, arr[i]});
    }
    
    cout<<ret;
    
    return 0;
}