백준 22866번 - 탑 보기

황제연·2024년 8월 21일
0

알고리즘

목록 보기
73/169
post-thumbnail

문제 탐색하기

입력값 자료 정리

  1. n은 입력으로 들어올 건물의 높이의 개수이다
  2. 이후 들어오는 값들은 각 건물의 높이값이다.

해결방법 추론

  1. 단순히 이중포문으로 해결하는 것이 가장 빠른 방법이겠지만,
    n의 최대 입력값이 10만이기 때문에 브루트포스는 불가능하다
  2. 각 건물마다 오른쪽에서 바라봤을 때와 왼쪽을 바라보았을 때의 최댓값을 구하기 위해서는
    n만큼의 탐색이 필요하다
  3. 그렇지만 이중포문으로는 불가능하니 각각을 나눠서 탐색한다고 했을 때,
    다른 방법이 없을까 고민했다
  4. 내가 바라보는 방향에서 내가 지금 알고 있는 높은 건물보다 더 높은 건물을 발견하면,
    갱신하는 방식은 마치 백준 1863번과 유사하다
  5. 즉 스택을 활용하면, n번의 탐색을 각 방향으로 두번해서 해결할 수 있지않을까 생각하였다

출력값 상태 관리 설계

  1. 이제 해결방법의 틀을 잡았으니, 먼저 정답을 위한 출력값 상태를 어떻게 관리할지를 생각하자
  2. 정답으로 내가 바라볼 수 있는 모든 건물의 수와 가장 가까운 건물의 위치를 각각 출력해야한다
  3. 또한 건물의 수가 0이면 위치는 출력하면 안 된다
  4. 이러한 조건속에서 각 값들을 관리하기 위해서는 고정된 건물 위치마다
    그 상태를 알 수 있는 배열로 관리하면 되겠다고 생각하였다
  5. 따라서 건물의 수와 가까운 건물의 위치를 보관할 각 배열을 하나씩 선언하였다
  6. 그리고 가까운 건물의 위치의 경우는 나올 수 있는 높이 최댓값을 음수로 바꾼뒤,
    -1을 더한 -100,001로 선언하였다
  7. 이렇게 하지 않으면, 첫 탐색에서 값을 갱신하지 않았을 때
    두번째 탐색에서 가까운 거리를 갱신하지 못한다
  8. 예를들어 0으로 초기화가 되어있다면 이후 가까운 거리를 발견하더라도 무조건 0으로 고정될 것이다
  9. 따라서 7번과 같이 초기화를 진행해야한다.

스택에 넣을 값 고민

  1. 그럼 이제 스택에 뭘 넣어야할까 고민을 해야한다
  2. 두가지가 후보로 존재한다. 각 건물의 높이와 그 위치다
  3. 하지만 이 문제에서는 가장 가까운 건물의 위치를 물어보았으므로,
    스택에 건물의 위치를 넣기로 결정했다

공통 탐색과정 설계

  1. n만큼 탐색하면서 각 위치를 스택에 넣어준다.
  2. 스택에 넣어주기 이전에 먼저 스택이 비어있지 않은지 체크하고,
    현재 위치의 높이가 스택의 peek()위치의 높이보다 작은지도 체크한다
  3. 만약 두 조건을 모두 만족시킨다면, 조건이 만족되는 동안 스택의 값을 빼준다

왼쪽을 바라보았을 때

  1. 공통 탐색과정 속에서 각 방향 탐색에 대한 추가 설계가 필요하다
  2. 일단 스택의 크기는 현재 위치에서 왼쪽을 바라보았을 때, 볼 수 있는 건물의 개수이다
  3. 스택의 크기를 출력값 상태를 관리하는 배열에 초기화해주자
  4. 이어서 만약 하나라도 바라볼 수 있는 건물이 있다면,
    스택의 peek에 가장 가까운 건물의 위치가 담겨 있기 때문에
    출력값 상태를 관리하는 배열에 초기화해주자

오른쪽을 바라보았을 때

  1. 역으로 뒤에서부터 탐색을 한다
  2. 공통 탐색 과정속에서 마찬가지로 추가 설계가 필요하다
  3. 이번에는 스택의 크기를 출력값 상태를 관리하는 배열에 더해주자.
    이전 탐색 과정의 값이 들어있기 때문이다
  4. 이어서 이번 탐색의 결과인 스택이 비어있지 않고,
    오른쪽을 바라보았을 때 현재 위치까지의 거리와
    왼쪽을 바라보았을 때 현재 위치까지의 거리를 비교하여,
    만약 전자가 더 작다면 스택의 peek값으로 출력값 상태를 관리하는 배열에 갱신해준다

시간복잡도 계산

  1. 이 모든 과정의 시간복잡도는 n만큼의 탐색만으로 결정난다
  2. 따라서 시간복잡도는 O(n)이 되고 시간제한안에 문제를 해결할 수 있다

코드 설계하기

입력값 상태 관리하기

  1. n은 변수로, 이후 들어오는 건물의 높이는 int 타입의 배열로 관리한다

출력값 상태 관리 및 초기값 설정

  1. int타입의 배열로 출력을 위한 값들의 상태를 관리한다
  2. ans 배열에는 바라볼 수 있는 가장 가까운 건물의 위치를,
    tops 배열에는 양 방향으로 바라볼 수 있는 건물의 개수를 보관한다
  3. 입력과정에서 ans 배열도 -100,001로 초기화를 해준다

탐색 과정 설계 1

  1. 0~n-1까지의 탐색과정 전에 스택을 미리 초기화해준다
  2. 설계한대로 과정을 진행해준다

탐색 과정 설계 2

  1. n-1~0까지의 탐색을 진행하며, 미리 스택을 초기화해준다
  2. 앞서 설계한대로 과정을 진행해준다
  3. 한가지 구체적으로 코드를 설계해야할 부분이 있는데,
    이전 탐색 과정과 현재 탐색과정 중 발견한 건물의 거리 중 더 가까운 건물을 비교하는 조건이다
  4. stack.peek()-i < i - ans[i]로 구현하였다
  5. 절댓값을 추가할 필요가 없는데, stack.peek()는 배열의 끝에서부터 탐색하므로 i보다 무조건 크며,
    ans[i]는 배열의 처음부터 탐색한 결과이므로 끝에서부터 탐색하는 현재의 i보다 무조건 작다.
  6. 따라서 절댓값을 추가할 필요가 없으며, 설계한대로 구현하면 된다

출력 과정 설계

  1. 출력 과정은 n만큼 순회하면서 각각 출력값 상태를 관리하는 배열의 값을 출력한다
  2. 먼저 tops[i]의 배열 값을 출력한다
  3. 이어서 ans[i]의 배열 값을 출력하는데, tops[i]가 0보다 큰 경우에만 출력해준다
  4. 이렇게 출력해주면 정답이 된다.

정답 코드

import java.io.*;
import java.util.*;


public class Main {

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        Stack<Integer> stack = new Stack<>();

        int n = Integer.parseInt(br.readLine());
        int[] arr = new int[n];
        StringTokenizer st = new StringTokenizer(br.readLine());
        int[] ans = new int[n];
        int[] tops = new int[n];
        for (int i = 0; i < n; i++) {
            arr[i] = Integer.parseInt(st.nextToken());
            ans[i] = -100001;
        }

        for (int i = 0; i < n; i++) {
            while(!stack.isEmpty() && arr[stack.peek()] <= arr[i]){
                stack.pop();
            }
            tops[i] = stack.size();
            if(tops[i] > 0){
                ans[i] = stack.peek();
            }
            stack.push(i);
        }

        stack = new Stack<>();
        for (int i = n-1; i >= 0; i--) {
            while(!stack.isEmpty() && arr[stack.peek()] <= arr[i]){
                stack.pop();
            }

            tops[i] += stack.size();
            if(!stack.isEmpty() && stack.peek() - i < i - ans[i]){
                ans[i] = stack.peek();
            }
            stack.push(i);
        }

        for (int i = 0; i < n; i++) {
            bw.write(tops[i] + " ");
            if(tops[i] > 0){
                bw.write((ans[i]+1)+"");
            }
            bw.write("\n");
        }

        br.close();
        bw.close();
    }
}

문제 링크

22866번 - 탑 보기

profile
Software Developer

0개의 댓글