[백준] 17298 - 오큰수 (재귀 풀이)

2021. 6. 22. 14:35·알고리즘 (PS)/BOJ
반응형

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

 

17298번: 오큰수

첫째 줄에 수열 A의 크기 N (1 ≤ N ≤ 1,000,000)이 주어진다. 둘째에 수열 A의 원소 A1, A2, ..., AN (1 ≤ Ai ≤ 1,000,000)이 주어진다.

www.acmicpc.net

문제 설명을 간단하게 하면, 내 오른쪽에 있는 숫자들 중, 나보다 큰 첫번째 숫자를 찾는 문제이다.

 

5 2 3 8 이면,

5 입장에서 오른쪽에서 가장 처음만나는 자기보다 큰 숫자는 8,

2 입장에서도 8

3 입장에서도 8이다.

 

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

 

14659번: 한조서열정리하고옴ㅋㅋ

첫째 줄에 봉우리의 수 겸 활잡이의 수 N이 주어진다. (1 ≤ N ≤ 30,000) 둘째 줄에 N개 봉우리의 높이가 왼쪽 봉우리부터 순서대로 주어진다. (1 ≤ 높이 ≤ 100,000) 각각 봉우리의 높이는 중복 없이

www.acmicpc.net

이 문제를 풀고 오면 이해하기가 편하다.

난이도가 쉬운 아래 문제를 풀고, 이 문제를 푼다면 좀 더 편하게 느껴질 것이다.

 

가장 먼저 떠올릴 수 있는 방법은 각각의 숫자마다 자기 오른쪽을 다 체크하는 방법일 것이다.

하지만 이렇게 풀면 시간복잡도가 O(n^2) 이므로, 최대 100개의 입력을 받을 수 있는 위 문제에서는

시간초과를 피할 수가 없다.

 

따라서 나는 선형적으로 한번 체크한 숫자를 한번 더 체크하지 않는 방식으로 푸는 방법을 고민했다.

 

맨 처음에 떠올린 방법은 다음과 같았다.

왼쪽에서 숫자를 하나 체크하고, 해당 숫자보다 작은 숫자를 만나는 동안 카운트를 센다.

그 후 자기보다 큰 숫자를 만나면 그동안 카운트를 센 숫자들은 모두 오큰수를 해당 큰 수로 설정한다.

 

5 4 2 1 6

다음과 같은 숫자가 있다고 한다면, 맨 처음 5를 저장해두고,

5보다 큰 숫자가 나올 때까지 지나간 숫자들은 모두 카운트를 세고 넘긴다.

5보다 큰 숫자인 6을 만나는 순간, 지금까지 센 카운트 4와, 이 4개 숫자에 대한 오큰수 6을 따로 저장한다.

이렇게 카운트 리스트, 오큰수 리스트를 모아놓고

나중에 출력할 땐, 오큰수 리스트에 있는 수를, 같은 인덱스의 카운트 리스트 숫자만큼 반복해서 출력한다.

 

n = int(input())
nums = list(map(int, input().split()))
sort_nums = sorted(nums)
count = []
oknum = []
now_count = 0
now_num = -1

for i in range(n):
    if now_num == -1:
        now_num = nums[i]
        now_count += 1
    elif now_num >= nums[i]:
        now_count += 1

    else:
        count.append(now_count)
        oknum.append(nums[i])
        now_count = 1
        now_num = nums[i]

if now_count > 0:
    count.append(now_count)
    oknum.append(-1)

for i in range(len(count)):
    for j in range(count[i]):
        print(oknum[i], end=' ')

하지만 이 코드의 반례는 너무나도 쉽게 나온다.

5 1 2 4 6

이렇게 수열이 있다고 해보자.

 

그렇다면 위 알고리즘은 아까와 마찬가지로 작동하면서 출력을

6 6 6 6 -1

이렇게 할 것이다.

하지만 정답은

6 2 4 6 -1 이다.

 

5보다 작은 숫자들이 5와 같은 오큰수를 가진다는 보장이 되지 않는 것이 이 알고리즘의 문제다.

 

그래서 이 문제를 해결하기 위해 어떻게 할지 고민하다가 다음과 같은 알고리즘을 떠올렸다.

우선 이 알고리즘을 떠올리는데는 아까 소개했던 한조 문제가 도움이 되었다.

 

이 문제를 그림으로 도식화하면 다음과 같다.

화살표로 가리키는 숫자가 오큰수이다.

나는 이 문제를 거꾸로 생각해보았다.

 

거꾸로 7에서부터 돌면서, 자기 자신을 오큰수로 가지게 되는 막대를 찾는 것이다.

이때, 자기보다 작은 수를 만나면, 해당 수 입장에서 다시 자기 자신을 오큰수로 가지는 수를 찾는다.

그러다가 자기 보다 큰 수를 만나면, 더이상 자기는 오큰수가 될 수 없기 때문에, 멈추고,

자기 이전 오큰수를 찾는 수에게 이어서 오큰수를 찾도록 한다.

 

말로 풀어 설명하면 복잡해보이지만 간단하게 위 예시를 사용하여 정리하면 다음과 같다.

 

1. 7 입장에서 거꾸로 탐색을 시작한다.

2. 자기(7)보다 작은 수(2)를 만나면, 해당 수의 오큰수를 자기(7)로 설정한다.

 

3. 재귀적으로 2입장에서 거꾸로 탐색을 시작한다.

4. 자기(2)보다 작은 수를 만나면, 해당수의 오큰수를 자기(2)로 설정한다.

5. 만약 만나지 못했다면 자신의 인덱스를 return 하고 함수를 종료한다.

 

6. 7 입장에서, return 받은 인덱스부터 다시 탐색을 진행한다.

7. 자기(7)보다 작은 수(4)를 만나면 해당 수의 오큰수를 자기(7)로 설정한다.

 

8. 재귀적으로 4 입장에서 거꾸로 탐색을 시작한다.

... 이하 반복

 

이렇게 문제를 풀면, 한번 방문했던 수를 두번다시 방문하지 않는다.

 

즉, 나보다 작은 수를 만나면, 그 수에게 자기보다 작은 수의 처리를 맡기고

그 수가 처리한 결과를 돌려주면, 나는 그 수가 처리한 결과 이후부터 처리해나가면 되는 것이다.

 

이 알고리즘을 코드로 구현하면 다음과 같이 구현된다.

 

import sys
sys.setrecursionlimit(10**8)
def f(index):
    num = nums[index]
    while True:
        if index == 0:
            return -1
        index -= 1
        if nums[index] < num:
            oken[index] = num
            index = f(index) + 1
        else:
            return index

n = int(input())
nums = list(map(int, input().split()))
oken = [-1 for _ in range(n)]
index = n-1
while True:
    index = f(index) + 1
    if index == 0:
        break
    index -= 1
print(*oken)

 

위 코드로 AC를 받을 수 있었다.

반응형
저작자표시 비영리 변경금지 (새창열림)

'알고리즘 (PS) > BOJ' 카테고리의 다른 글

[백준] 1043 - 거짓말 (분리집합 풀이)  (0) 2021.06.30
[백준] 1005 - ACM Craft  (0) 2021.06.29
[백준] 9938 - 방청소  (0) 2021.05.05
[백준] 5719 - 거의 최단 경로  (0) 2021.04.17
[백준] 2662 - 기업 투자  (0) 2021.04.06
'알고리즘 (PS)/BOJ' 카테고리의 다른 글
  • [백준] 1043 - 거짓말 (분리집합 풀이)
  • [백준] 1005 - ACM Craft
  • [백준] 9938 - 방청소
  • [백준] 5719 - 거의 최단 경로
에버듀
에버듀
개발은 좋은데 뭘로 개발할까
  • 에버듀
    Blog. 에버듀
    에버듀
  • 전체
    오늘
    어제
    • 분류 전체보기 (615) N
      • 개인 프로젝트 (43)
        • 토이 프로젝트 (3)
        • [2020] 카카오톡 봇 (9)
        • [2021] 코드악보 공유APP (22)
        • [2022] 유튜브 뮤직 클론코딩 (9)
        • [2025] 한글 SQL 데이터베이스 (0)
      • 팀 프로젝트 (22)
        • [2020] 인공지능 숫자야구 (4)
        • [2022] OSAM 온라인 해커톤 (10)
        • [2024] GDSC 프로젝트 트랙 (6)
        • [2025] 큰소리 웹 페이지 (2)
      • CS (335)
        • 자료구조 (19)
        • 어셈블리 (41)
        • 멀티미디어응용수학 (7)
        • 컴퓨터 구조 (29)
        • 알고리즘 분석 (4)
        • 컴퓨터 네트워크 (38)
        • 프로그래밍언어론 (15)
        • HCI 윈도우즈프로그래밍 (26)
        • 기초데이터베이스 (29)
        • 운영체제 (23)
        • 오토마타 (24)
        • 문제해결기법 (11)
        • 블록체인 (22)
        • 소프트웨어공학 (21)
        • 기계학습심화 (12)
        • 컴퓨터그래픽스와 메타버스 (8)
        • 분산시스템특론 (6)
      • 자기계발 (45) N
        • 생각 정리 (23) N
        • 대외활동 (11)
        • 동아리 (7)
        • 자격증 (3)
        • 머니 스터디 (1)
      • 알고리즘 (PS) (107)
        • BOJ (101)
        • Programmers (5)
        • 알고리즘 이모저모 (1)
      • WEB(BE) (8)
        • express.js (1)
        • Spring & Spring Boot (7)
      • WEB(FE) (2)
        • html, css, js (1)
        • React.js (1)
      • Tool & Language (6)
        • Edit Plus (1)
        • Git (1)
        • Python3 (2)
        • Java (2)
      • Infra (12)
        • AWS (1)
        • Oracle Cloud (8)
        • Firebase (2)
        • Network (1)
      • Android (18)
        • Java (6)
        • Flutter (12)
      • Window (2)
        • Visual Studio 없이 WPF (1)
        • MFC (1)
      • 독서 (14)
        • Inside Javascript (7)
        • Database Internals (6)
        • 한 글 후기 (1)
  • 링크

    • github
    • website
  • 인기 글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
에버듀
[백준] 17298 - 오큰수 (재귀 풀이)
상단으로

티스토리툴바