[백준] 1038 - 감소하는 수(G5) : DP, BFS 풀이

2022. 1. 3. 15:04·알고리즘 (PS)/BOJ
반응형

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

 

1038번: 감소하는 수

음이 아닌 정수 X의 자릿수가 가장 큰 자릿수부터 작은 자릿수까지 감소한다면, 그 수를 감소하는 수라고 한다. 예를 들어, 321과 950은 감소하는 수지만, 322와 958은 아니다. N번째 감소하는 수를

www.acmicpc.net

쓸데없이 힘들게 푼 문제이다.

알고리즘 분류가 백트래킹에 브루트포스였는데, 그렇게 풀었으면 더 쉽게 풀었을 것 같다.

전체 경우의 수가 1023가지 밖에 되지 않아서, 그냥 모든 케이스를 순서대로 방문해보면 된다.

각 숫자가 중복으로 쓰일 수 없어서 비트마스킹을 활용한 풀이도 있었다.

 

나는 DP 에 역추적을 써서 풀었다.

정말 빙빙 돌아서 풀었다 ㅋㅋㅋ

 

내가 푼 풀이는 2차원 DP 테이블을 아래 정의로 만든다.

 

dp[i][j] = i자리 숫자중 첫번째 자리가 j로 시작하는 감소하는 수의 개수

 

그럼 dp[i][j] = dp[i-1][j-1] + dp[i][j-1] 점화식을 이용해 DP테이블을 채울 수 있다.

이 DP 테이블의 값을 이용해 역추적을 통해 답을 구하고자 하였다.

 

dp 테이블을 (0,0) 부터 시작해 카운트를 dp테이블 값만큼 늘려나가다가

만약 카운트가 찾는 숫자 이상이 되면, 그 순간의 길이와 첫 자리가, 정답인 숫자의 길이와 첫 자리가된다.

그 다음 자리의 숫자를 찾기 위해

 

(찾는 수 - 찾는 수를 넘기 직전의 카운트) + 찾는 자리수보다 한자리 적은 자릿수까지의 누적 개수

 

의 값을 찾는다고 생각하고 다시 위 과정을 해당 값에 대해 반복한다.

(찾는 수 = 찾는 수를 넘기 직전의 카운트) 를 하면, 그 결과로 i자리수 중 j 로 시작하는 숫자들 중

찾는 수자가 몇번째 숫자인지 정보를 알 수 있다.

 

찾는 자리수보다 한자리 적은 자릿수까지의 누적 개수

 

이 값을 더해주는 이유는, 첫번째 자리 숫자를 찾았으면, 그 다음 자릿수를 찾아야 하니,

그걸 위해 시작점을 맞춰주는 역할이다.


위 알고리즘을 구현한 코드이다.

def find(_n):
    count, last_count = 0, 0
    for length in range(1, 11):
        for first in range(10):
            last_count = count
            count += dp[length][first]
            if count >= _n:
                break

        if count >= _n:
            break

    if count < _n: ## 다 셌는데도 카운트가 모자라면 안되는 거
        return -1

    if length == 1:
        return str(first)

    return str(first) + find(row_count[length-2] + (_n - last_count))


n = int(input())
dp = [[0 for _ in range(10)] for _ in range(11)]
row_count = [0]
for i in range(10):
    dp[1][i] = 1
row_count.append(sum(dp[1]))

for i in range(2, 11):
    for j in range(1, 10):
        dp[i][j] = dp[i-1][j-1] + dp[i][j-1]
    row_count.append(row_count[-1] + sum(dp[i]))

print(find(n+1))

# 테스트로 모든 수를 출력해봤다. (애초에 이게 가능한 시점에서 브루트포스를 의심했어야 했다.)
#for i in range(1, 1024):
#    print(i, find(i))

 

정말 쓸데없이 복잡한 알고리즘인데, 차라리 이걸 더 간단하게 하면

함수를 i 번째 자리인 숫자중에서 j 번째 숫자를 찾도록 하는게 더 나았을 것 같다.

(첫자리부터 마지막 자리 숫자까지 하나씩 찾는 과정에서, DP테이블을 중복으로 방문하기 때문이다.)

 

def find(length, _n):
    count, last_count = 0, 0
    for first in range(10):
        last_count = count
        count += dp[length][first]
        if count >= _n:
            break

    if length == 1:
        return str(first)

    return str(first) + find(length - 1, _n - last_count)


n = int(input())
dp = [[0 for _ in range(10)] for _ in range(11)]
row_count = [0]
for i in range(10):
    dp[1][i] = 1
row_count.append(sum(dp[1]))

for i in range(2, 11):
    for j in range(1, 10):
        dp[i][j] = dp[i-1][j-1] + dp[i][j-1]
    row_count.append(row_count[-1] + sum(dp[i]))

for i in range(1, 11):
    if n+1 <= row_count[i]:
        print(find(i, n+1 - row_count[i-1]))
        exit()

print(-1)

 

아까 코드를 조금 수정했다.

이걸로 i 번째 자릿수를 구할 땐 해당 자리수의 dp 열만 체크하도록 하여 시간을 좀 더 줄여보았다.

 

 

8ms를 더 줄일 수 있었다.

같은 코드를 pypy로 제출해보니 오히려 오래걸렸다.


백트래킹 아이디어로 한번 풀어보려고 했는데, 재귀로 구현하다보니까 숫자 순서대로 방문하는게 안되는 문제가 있었다.

0 -> 1 -> 10 -> 2 -> 20 -> 21 -> 3 ->  이런식으로 방문하는 것이었다.

 

그런데 이걸 보다보니 이건 DFS와 BFS의 차이와 똑같았다.

그래서 그냥 BFS로 구현했다.

 

from collections import deque
d = deque()
count = -1
n = int(input())
for i in range(10):
    d.append(i)
while d:
    now_num = d.popleft()
    count += 1

    if count == n:
        print(now_num)
        exit()

    for i in range(now_num%10):
        d.append(now_num*10 + i)

print(-1)

 

매우 직관적이고 엄청 간단해졌다.

 

근데 백트래킹으로 푸는건 정말 안떠오른다...

아무래도 다시 기초 알고리즘을 복습해야 할 것 같다...

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

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

[백준] 23059- 리그 오브 레게노 (G2)  (0) 2022.01.07
[백준] 15683 - 감시 (G5)  (0) 2022.01.05
[백준] 15778 - Yut Nori (P4)  (0) 2021.12.26
[백준] 1644 - 소수의 연속합 (G3)  (0) 2021.12.23
[백준] 2098 - 외판원 순환 (G1)  (0) 2021.12.22
'알고리즘 (PS)/BOJ' 카테고리의 다른 글
  • [백준] 23059- 리그 오브 레게노 (G2)
  • [백준] 15683 - 감시 (G5)
  • [백준] 15778 - Yut Nori (P4)
  • [백준] 1644 - 소수의 연속합 (G3)
에버듀
에버듀
개발은 좋은데 뭘로 개발할까
  • 에버듀
    Blog. 에버듀
    에버듀
  • 전체
    오늘
    어제
    • 분류 전체보기 (614) 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)
      • 자기계발 (44)
        • 생각 정리 (22)
        • 대외활동 (11)
        • 동아리 (7)
        • 자격증 (3)
        • 머니 스터디 (1)
      • 알고리즘 (PS) (107)
        • BOJ (101)
        • Programmers (5)
        • 알고리즘 이모저모 (1)
      • WEB(BE) (8) N
        • express.js (1)
        • Spring & Spring Boot (7) N
      • 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
에버듀
[백준] 1038 - 감소하는 수(G5) : DP, BFS 풀이
상단으로

티스토리툴바