728x90

트리 순회

2 초 128 MB 53523 34550 26670 66.911%

문제

이진 트리를 입력받아 전위 순회(preorder traversal), 중위 순회(inorder traversal), 후위 순회(postorder traversal)한 결과를 출력하는 프로그램을 작성하시오.

예를 들어 위와 같은 이진 트리가 입력되면,

  • 전위 순회한 결과 : ABDCEFG // (루트) (왼쪽 자식) (오른쪽 자식)
  • 중위 순회한 결과 : DBAECFG // (왼쪽 자식) (루트) (오른쪽 자식)
  • 후위 순회한 결과 : DBEGFCA // (왼쪽 자식) (오른쪽 자식) (루트)

가 된다.

입력

첫째 줄에는 이진 트리의 노드의 개수 N(1 ≤ N ≤ 26)이 주어진다. 둘째 줄부터 N개의 줄에 걸쳐 각 노드와 그의 왼쪽 자식 노드, 오른쪽 자식 노드가 주어진다. 노드의 이름은 A부터 차례대로 알파벳 대문자로 매겨지며, 항상 A가 루트 노드가 된다. 자식 노드가 없는 경우에는 .으로 표현한다.

출력

첫째 줄에 전위 순회, 둘째 줄에 중위 순회, 셋째 줄에 후위 순회한 결과를 출력한다. 각 줄에 N개의 알파벳을 공백 없이 출력하면 된다.

예제 입력 1

7
A B C
B D .
C E F
E . .
F . G
D . .
G . .

예제 출력 1

ABDCEFG
DBAECFG
DBEGFCA

 

트리와 트리 순회에 대한 이해가 되어있다면 쉽게 풀 수 있는 문제이다.

 

해당 문제를 풀기 위하여 딕셔너리(dictionary) 자료구조를 이용하여 트리를 구현하였다.

문제에서 부모/왼쪽자식/오른쪽 자식의 순서로 데이터가 제공되므로 입력받은 데이터를 바탕으로

부모를 딕셔너리의 key로 삼고 자식은 2개 이하이므로, 배열의 형태로 입력받는다.

# 데이터 입력받기
nodes = input().split()
# 루트 : 왼쪽자식, 오른쪽자식으로 저장
tree[nodes[0]] = [nodes[1], nodes[2]]
 

모든 결과를 입력받고, 문제의 조건에 맞춰 전위순회, 중위순회, 후위순회한 결과값을 출력한다.

 

전체 코드는 아래와 같다.

# https://www.acmicpc.net/problem/1991

n = int(input())

# 트리로 사용할 딕셔너리 생성
tree = {}


# 전위순회 VLR
def preorder(root):
    # .이 아닐때만 출력
    if root != '.':
        print(root, end='')  # v
        preorder(tree[root][0])  # L
        preorder(tree[root][1])  # R


# 중위순회 LVR
def inorder(root):
    # .이 아닐때만 출력
    if root != '.':
        inorder(tree[root][0])  # L
        print(root, end='')  # v
        inorder(tree[root][1])  # R


# 후위순회 LRV
def postorder(root):
    # .이 아닐때만 출력
    if root != '.':
        postorder(tree[root][0])  # L
        postorder(tree[root][1])  # R
        print(root, end='')  # v


for _ in range(n):
    # 데이터 입력받기
    nodes = input().split()
    # 루트 : 왼쪽자식, 오른쪽자식으로 저장
    tree[nodes[0]] = [nodes[1], nodes[2]]

# 결과 출력
preorder('A')
print()
inorder('A')
print()
postorder('A')
 
728x90

숫자 카드


2 초 256 MB 102414 43067 31537 42.773%

문제

숫자 카드는 정수 하나가 적혀져 있는 카드이다. 상근이는 숫자 카드 N개를 가지고 있다. 정수 M개가 주어졌을 때, 이 수가 적혀있는 숫자 카드를 상근이가 가지고 있는지 아닌지를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 상근이가 가지고 있는 숫자 카드의 개수 N(1 ≤ N ≤ 500,000)이 주어진다. 둘째 줄에는 숫자 카드에 적혀있는 정수가 주어진다. 숫자 카드에 적혀있는 수는 -10,000,000보다 크거나 같고, 10,000,000보다 작거나 같다. 두 숫자 카드에 같은 수가 적혀있는 경우는 없다.

셋째 줄에는 M(1 ≤ M ≤ 500,000)이 주어진다. 넷째 줄에는 상근이가 가지고 있는 숫자 카드인지 아닌지를 구해야 할 M개의 정수가 주어지며, 이 수는 공백으로 구분되어져 있다. 이 수도 -10,000,000보다 크거나 같고, 10,000,000보다 작거나 같다

출력

첫째 줄에 입력으로 주어진 M개의 수에 대해서, 각 수가 적힌 숫자 카드를 상근이가 가지고 있으면 1을, 아니면 0을 공백으로 구분해 출력한다.

예제 입력 1

5
6 3 2 10 -10
8
10 9 -5 2 3 4 5 -10

예제 출력 1

1 0 0 1 1 0 0 1

이분탐색(이진탐색)을 이용하여 해결할수 있는 문제이다.

 

이분탐색은 탐색의 범위를 절반씩 좁혀나가면서 원하는 데이터가 존재하는지 탐색한다.

이분탐색을 적용하면 모든 요소를 하나씩 탐색하는 순차탐색보다 빠른 속도로 데이터를 탐색할 수 있기 때문에 탐색할 범위가 크다면, 순차탐색보다 이분탐색을 적용하는것이 좋다. 위 문제의 경우 탐색의 범위가 최대 50만까지 가능하므로 이분탐색을 적용하여 풀었다.

 

이분탐색에 대해 이해하고 있다면, 별도의 해설이 가능하지 않으므로 별도의 해설은 적지 않겠다.

# 카드의 개수 입력받기
n = int(input())

# 카드 리스트 입력받기
card_list = list(map(int, input().split()))

# 탐색할 카드의 개수 입력받기
m = int(input())

# 탐색할 카드 리스트 입력받기
search_list = list(map(int, input().split()))

# 이분 탐색을 이용하기 위해 카드리스트를 정렬한다.
card_list.sort()

for i in range(len(search_list)):
    # 탐색할 가드 선정
    card = search_list[i]

    # 이분탐색 시작
    start = 0  # 시작 인덱스
    end = len(card_list)  # 마지막 인덱스

    for j in range(start, end):

        # 탐색 종료 조건 설정
        if start >= end:
            # 탐색 실패
            search_list[i] = 0
            break

        # 가운데 인덱스 선택
        middle = (start+end)//2

        # 가운데 값이 탐색카드와 같을때 => 탐색종료
        if card == card_list[middle]:
            # 탐색 성공
            search_list[i] = 1
            break
        # 가운데 카드보다 탐색카드가 클때
        elif card > card_list[middle]:
            # 탐색 범위 수정
            start = middle+1

        # 가운데 카드보다 탐색카드가 작을때
        elif card < card_list[middle]:
            end = middle


# 탐색 결과 출력
for card in search_list:
    print(card, end=" ")
 

 

728x90

파도반 수열  

1 초 128 MB 97612 43574 35807 43.290%

문제

오른쪽 그림과 같이 삼각형이 나선 모양으로 놓여져 있다. 첫 삼각형은 정삼각형으로 변의 길이는 1이다. 그 다음에는 다음과 같은 과정으로 정삼각형을 계속 추가한다. 나선에서 가장 긴 변의 길이를 k라 했을 때, 그 변에 길이가 k인 정삼각형을 추가한다.

파도반 수열 P(N)은 나선에 있는 정삼각형의 변의 길이이다. P(1)부터 P(10)까지 첫 10개 숫자는 1, 1, 1, 2, 2, 3, 4, 5, 7, 9이다.

N이 주어졌을 때, P(N)을 구하는 프로그램을 작성하시오.

입력

첫째 줄에 테스트 케이스의 개수 T가 주어진다. 각 테스트 케이스는 한 줄로 이루어져 있고, N이 주어진다. (1 ≤ N ≤ 100)

출력

각 테스트 케이스마다 P(N)을 출력한다.

예제 입력 1

2
6
12

예제 출력 1

3
16

다이나믹 프로그래밍을 활용하여 해결할수 있는 문제이다.

 

문제에 대한 이해를 돕기 위해 아래 그림을 그려왔다.

그림에 적힌 숫자는 해당 삼각형을 그리는 순서를 의미한다.

1번 삼각형을 그린 후, 2번 삼각형을 그리고, 이어서 3번 삼각형을 그리는 식이다.

이때 가장 긴 변을 한 변으로 삼아 정삼각형을 그리게 되는데, 어떤식으로 삼각형이 그려지는지를 파악한다면 쉽게 점화식을 세울 수 있다.

우선 1,2,3번 삼각형은 각 변의 길이가 1로 그려지고, 4번, 5번 삼각형은 길이가 2로 그려진다.

규칙이 생기는것은 그 다음순번의 삼각형부터이다. 아래 그림을 살펴보자.

6번 삼각형은 5번 삼각형의 변과 1번 삼각형의 변의 합을 한 변으로 삼아 그려지고, 7번 삼각형은 6번 삼각형의 변과 2번 삼각형의 변의 합을 한 변으로 삼아 그려진다.

 

이를 수식으로 나타내면 다음과 같다.(dp[n]은 n번째 삼각형의 한 변의 길이를 의미한다.)

dp[6] = dp[5] + dp[1]

dp[7] = dp[6] + dp[2]

...

 

모든 수가 다음 규칙을 만족하므로, 아래와 같은 점화식을 세울 수 있다.

 

dp[i] = dp[i-1] + dp[i-5]

 

문제의 조건에 맞춰 코드를 짜면 다음과 같다.

# https://www.acmicpc.net/problem/9461

'''
오른쪽 그림과 같이 삼각형이 나선 모양으로 놓여져 있다. 첫 삼각형은 정삼각형으로 변의 길이는 1이다. 그 다음에는 다음과 같은 과정으로 정삼각형을 계속 추가한다. 나선에서 가장 긴 변의 길이를 k라 했을 때, 그 변에 길이가 k인 정삼각형을 추가한다.

파도반 수열 P(N)은 나선에 있는 정삼각형의 변의 길이이다. P(1)부터 P(10)까지 첫 10개 숫자는 1, 1, 1, 2, 2, 3, 4, 5, 7, 9이다.

N이 주어졌을 때, P(N)을 구하는 프로그램을 작성하시오.

입력
첫째 줄에 테스트 케이스의 개수 T가 주어진다. 각 테스트 케이스는 한 줄로 이루어져 있고,
N이 주어진다. (1 ≤ N ≤ 100)

출력
각 테스트 케이스마다 P(N)을 출력한다.

'''

# 테스트 케이스의 개수
t = int(input())

# dp 테이블 작성
# n은 최대 100
dp = [0]*101 # 0~100

# 이전 숫자에 영향을 받지 않는 데이터들은 미리 기록
dp[0] = 1
dp[1] = 1
dp[2] = 1
dp[3] = 2
dp[4] = 2

# 테스트 케이스만큼 반복
for _ in range(t):
    n = int(input())
    # 알고리즘 시작
    for i in range(5,n+1):
        if dp[i] != 0: # dp테이블에 이미 값이 기록된경우는 무시한다.
            continue
        # 점화식에 따라 아래와 같이 수행한다. 
        # => 5번째로 그리는 도형부터 이전숫자와 0번부터 올라가는 구조이므로
        dp[i] = dp[i-1] + dp[i-5] 
    
    print(dp[n-1])
 
728x90

가장 큰 증가하는 부분 수열  

1 초 256 MB 50953 22859 18182 44.385%

문제

수열 A가 주어졌을 때, 그 수열의 증가하는 부분 수열 중에서 합이 가장 큰 것을 구하는 프로그램을 작성하시오.

예를 들어, 수열 A = {1, 100, 2, 50, 60, 3, 5, 6, 7, 8} 인 경우에 합이 가장 큰 증가하는 부분 수열은 A = {1, 100, 2, 50, 60, 3, 5, 6, 7, 8} 이고, 합은 113이다.

입력

첫째 줄에 수열 A의 크기 N (1 ≤ N ≤ 1,000)이 주어진다.

둘째 줄에는 수열 A를 이루고 있는 Ai가 주어진다. (1 ≤ Ai ≤ 1,000)

출력

첫째 줄에 수열 A의 합이 가장 큰 증가하는 부분 수열의 합을 출력한다.

예제 입력 1

10
1 100 2 50 60 3 5 6 7 8

예제 출력 1

113

 

'백준 11053번 : 가장 긴 증가하는 부분 수열' 문제의 시리즈 문제이다.

먼저 문제를 이해해보자면 배열이 주어졌을때, 수열의 합이 최대가 되도록 하여 최대 합을 출력하는것이 문제의 조건이다.

 

'수열'이 되도록 하기 위해서 배열의 값들을 모두 조사하며 이전 값이 자기자신보다 작다면 수열이 된다.

예를 들어 1 100 2 50 60 3 5 6 7 8 과 같은 배열에서 1 2 50 60은 수열이 되고, 2,3,5,6,7,8도 수열이 된다.

 

문제는 이렇게 만들 수 있는 수열들 중에서 합이 가장 큰경우의 합을 출력하는것이 목표이므로, dp 테이블에 값을 작성한 후 최대값을 출력하도록 하면, 문제를 쉽게 해결할 수 있다.

 

문제를 해결하기 위해 생각해야 하는것은 다음과 같다.

 

  1. 이전값이 자기 자신보다 작을 것
  2. 1번을 만족하는 경우 자신의 값을 누적시켜 더 할것 => 이때 최대합을 구하는것이 목표이므로 자기자신보다 작은값들 중에서 누적합이 가장 큰 값을 더한다.

 

위 과정을 코드로 옮기면 아래와 같다.

n = int(input())

dp = [0]*(n)  # 누적합을 저장할 배열

data = list(map(int, input().split()))

dp[0] = data[0]  # 첫번째 dp의 값은 첫번째 데이터와 같음

# 입력받은 data의 각 인덱스에서 dp값이 가장 큰 인덱스에 현재 데이터를 누적시켜 더한다.

for i in range(1, n):
    for j in range(i):
        # 만족해야 하는 조건
        # 이전의 숫자들이 현재 숫자들보다 작은 숫자일것
        # 이전 숫자들중 가장 큰 값까지 이뤄지는 수열일것 => 그리고 이들의 누적합을 구해야함
        if data[j] < data[i] and dp[j] > dp[i]:
            dp[i] = dp[j]  # dp값 업데이트
    dp[i] += data[i]  # 현재 인덱스의 값을 누적합에 더한다.

print(max(dp))  # dp에 기록된 수중 가장 큰 값이, 가장 큰 합이 될 것
 

 

 

728x90

2×n 타일링 2 


1 초 256 MB 71346 42391 34114 58.844%

문제

2×n 직사각형을 1×2, 2×1과 2×2 타일로 채우는 방법의 수를 구하는 프로그램을 작성하시오.

아래 그림은 2×17 직사각형을 채운 한가지 예이다.

입력

첫째 줄에 n이 주어진다. (1 ≤ n ≤ 1,000)

출력

첫째 줄에 2×n 크기의 직사각형을 채우는 방법의 수를 10,007로 나눈 나머지를 출력한다.

예제 입력 1

2

예제 출력 1

3

예제 입력 2

8

예제 출력 2

171

예제 입력 3

12

예제 출력 3

2731

 

기본적인 아이디어는 백준 11726번 : 2xn 타일링 과 같다.

가로의 길이가 1, 2일때는 당연하게도 1, 3이 될것이고

 

가로의 길이가 3이상으로 넘어갈때부터는 두가지 선택지가 존재한다.

 

1. 2x2 블록에 2x1 일자블록(ㅣ)을 붙여서 2x3블록을 만든다.

=> dp[2]와 경우의 수가 같다.

 

2. 2x1블록에 2x1블록 2개(=) 혹은 2x2블록(ㅁ)을 붙여 2x3 블록을 만들 수 있다.

=> dp[1]와 경우의 수에 x2를 한 값과 같다. (=를 붙이거나, ㅁ을 붙이거나)

 

위의 과정을 통해 다음과 같은 점화식을 세울 수 있다.

 

dp[n] = dp[n-1] + dp[n-2] * 2 ( 단, dp[1] = 1 , dp[2] = 2, n>=3)

 

코드는 아래와 같다.

n = int(input())

# dp 테이블 작성
dp = [0]*(n+1)

for i in range(1, n+1):
    if i == 1:
        dp[i] = 1  # 2 x 1 크기는 1개
    elif i == 2:
        dp[i] = 3  # 2 x 2 크기는 3개(||,=,ㅁ)
    else:
        dp[i] = dp[i-1] + 2*dp[i-2]  # i-2인 경우는 =이 붙거나 ㅁ이 붙을수 있기 때문에

# 모듈러 연산하여 출력
print(dp[n] % 10007)
 

 

 

 

 

728x90

2×n 타일링  

1 초 256 MB 162675 62875 46628 36.594%

문제

2×n 크기의 직사각형을 1×2, 2×1 타일로 채우는 방법의 수를 구하는 프로그램을 작성하시오.

아래 그림은 2×5 크기의 직사각형을 채운 한 가지 방법의 예이다.

입력

첫째 줄에 n이 주어진다. (1 ≤ n ≤ 1,000)

출력

첫째 줄에 2×n 크기의 직사각형을 채우는 방법의 수를 10,007로 나눈 나머지를 출력한다.

예제 입력 1

2

예제 출력 1

2

예제 입력 2

9

예제 출력 2

55

 

 

그림을 직접 그려보면 쉽게 풀 수 있는 문제이다.

 

먼저, 문제에 대한 이해를 해보자.

2 x n 타일이라는 것은 타일의 높이가 2이고 가로의 길이가 n인 직사각형 타일을 의미한다.

이러한 형태를 만들기 위해 우리가 사용할 수 있는 재료는 2x1, 1x2 타일 2가지이다.

 

규칙을 파악하기 위해

n의 값을 1부터 3로 하여 직접 그림을 그려보자.

대표사진 삭제

사진 설명을 입력하세요.

 

위의 그림을 통해 규칙을 파악하였는가? 만약 규칙을 파악하지 못하였다면 아래 이미지를 보도록 하자

대표사진 삭제

사진 설명을 입력하세요.

가로의 길이가 n일때의 타일을 채울 수 있는 경우의 수를 dp[n]이라고 해보자.

dp[1]과 dp[2]는 자명하게도 각각 1과 2가 될 것이다.

 

색을 칠해놓은 위의 그림을 통해 확인할 수 있듯이,

2x3을 만드는 방법은 2x2 타일에 2x1 타일(ㅣ) 하나를 붙여서 만들거나, 2x1타일에 1x2타일 2개(=)를 가로로 놓아 만들 수 있다.

따라서 dp[3]은 dp[2]와 dp[1]의 합과 같게 된다.

 

이를 통해 다음과 같은 식을 세울 수 있다.

dp[n] = dp[n-1] + dp[n-2]

 

위의 과정을 코드로 옮기면 다음과 같다.

 

n = int(input())

# dp테이블 생성
dp = [0]*(n+1)

# n이 3이상인 경우에 대해서는 다음과 같이 생각할수 있다
# 2 x (n-1)인 도형에 일자 막대를 붙이는 경우 => dp[n-1]과 개수가 같다(일자 막대를 이어붙일 뿐이므로)
# 2 x (n-2)인 도형에 가로막대 2개(=모양)을 붙이는 경우 => dp[n-2]과 개수가 같다
# 따라서 점화식은 다음과 같다 dp[n] = dp[n-1] + dp[n-2]

for i in range(1, n+1):
    if i == 1:
        # 2 x 1인 도형을 만들 수 있는 경우의 수
        dp[i] = 1
    elif i == 2:
        # 2 x 2인 도형을 만들 수 있는 경우의 수
        dp[i] = 2  # ||,= 두가지 형태가 가능
    else:
        dp[i] = (dp[i-1] + dp[i-2]) % 10007  # 모듈러 연산하여 저장(문제의 조건)

print(dp[n] % 10007)
 

 

728x90

쉬운 계단 수

시간 제한
메모리 제한
제출
정답
맞힌 사람
정답 비율
1 초
256 MB
116961
37002
26724
29.848%

 

문제

45656이란 수를 보자.

이 수는 인접한 모든 자리의 차이가 1이다. 이런 수를 계단 수라고 한다.

N이 주어질 때, 길이가 N인 계단 수가 총 몇 개 있는지 구해보자. 0으로 시작하는 수는 계단수가 아니다.

 

입력

첫째 줄에 N이 주어진다. N은 1보다 크거나 같고, 100보다 작거나 같은 자연수이다.

 

출력

첫째 줄에 정답을 1,000,000,000으로 나눈 나머지를 출력한다.

 

예제 입력 1

1

 

예제 출력 1

9

 

예제 입력 2

2

 

예제 출력 2

 
17

 

'백준 11057번 : 오르막 수' 를 해결하였다면, 쉽게 풀 수 있을 것 같은 문제이다.

문제를 해결하기 위한 아이디어를 차례대로 정리해보겠다.

 

1. 테스트 케이스를 살펴보자

 

1 => 9 : 한 자리 계단 수는 9개(1,2,3,...,9) 이다.

2 => 17 : 두 자리 계단 수는 17개 이다. => 앞자리 수가 1부터 가능하므로 1/0,2 , 2/1,3 ... 8/7, 9 , 9/8 의 숫자로 구성되어 앞자리 수가 9인 경우를 제외하고 모두 앞자리수를 +1 혹은 -1 한 수로 쌍을 이루고 있으므로 2*8 + 1 = 17 이 된다.

 

위의 두 수를 통해 다음과 같은 가설을 세웠다.

 

앞의 자리수가 9인 경우를 제외하고 +1,-1을 한 수를 앞의 자리로 하는 계단수가 뒤에 올 수 있다. 이는 두자리 수가 아닌, 세자리 수인 경우도, 네자리 수인 경우도 마찬가지 일것이다.

 

예를 들어 세자리 계단 수를 구한다고 가정해보면 맨 앞자리 수가 1인 경우는 1 _ _과 같이 표현 할 수 있다. 이러한 경우 계단 수의 개수는 앞 자리수가 0인 2자리 계단 수의 개수와 앞자리 수가 2인 2자리 계단 수의 개수의 합과 같을 것이다.

 

문제의 조건에 따라 앞자리 수가 0인 계단수는 존재할 수 없으나, 앞자리수가 1인 경우 뒤에는 0인 계단수가 올수있으므로, 편의상 기록해뒀다가 최종 합을 구하는 과정에서 제거하면 될 것이다.

 

또한 앞자리 수가 9인 세자리 계단수의 개수를 살펴보면, 9 _ _ 과 같은 형태에서 다음에 올 수 있는 수는 8밖에 존재하지 않는다.

따라서 앞자리 수가 9인 세자리 계단수의 개수는 앞자리 수가 8인 두자리 계단수의 개수와 같다.

 

앞자리 수가 0과 9인 경우에 대해서만 +1, -1인 쌍이 존재하지 않는것이므로 조건문을 활용하여 별도로 값을 기록하면 될 것이다.

 

2. dp 테이블을 어떻게 작성할지 고민해보자.

 

앞서 생각한대로 코드를 작성하기 위해 내가 구상한 형태는 다음과 같다.

dp[n][i] => n자리수의 경우 앞자리 수가 i일때의 계단수의 개수

 

구상한 dp테이블로 점화식을 구성해보면 다음과 같다.

 

if n!=1 and n!=2

dp[n][i] = dp[n-1][i-1] + dp[n-1][i+1]

 

위 과정을 코드로 변환하면 다음과 같다.

n = int(input())

# 앞자리 수가 i인 n자리 계단수의 개수는 다음과 같이 표현한다
# dp[n][i]
# 이때 i는 1~9까지 가능함

# 수를 저장할 2차원 배열 작성
# n은 1보다 크고 100보다 작거나 같은 수
dp = [[0]*10 for _ in range(101)]

# 한자리 계단 수의 개수 기록
for i in range(10):
    dp[1][i] = 1
    
# 알고리즘 수행

# n자리 수가 될때까지 계단수 dp테이블에 작성
for i in range(2, n+1):  # 3~n자리 계단수 계산
    # 앞자리수가 될 수 있는 조건은 1~9까지의 수 => i에 대입하여 기록
    # 다음 수의 계산을 위해 앞자리수가 0인 계단수도 기록은 해둔다.
    for j in range(10):
        # 앞자리수가 0인 경위와 9인 경우는 뒤의수가 1과 8만이 가능하므로 별도로 기록한다.
        if j == 0:
            dp[i][j] = dp[i-1][1]  # 1인 경우만 가능
        elif j == 9:
            dp[i][j] = dp[i-1][8]  # 8인 경우만 가능
        else:
            # 2인 경우 1과 3이 가능(+-1인 쌍이 존재) 
            dp[i][j] = dp[i-1][j-1] + dp[i-1][j+1]

# => 오버플로우 방지를 위하여 %1000000000 하여 출력
# 결과 출력 n자리 계단수들의 합 => 앞자리 수가 0인 경우는 계단수로 취급하지 않으므로 빼준다.
print((sum(dp[n])-dp[n][0]) % 1000000000)
 

 

 

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

 

10844번: 쉬운 계단 수

첫째 줄에 정답을 1,000,000,000으로 나눈 나머지를 출력한다.

www.acmicpc.net

 

728x90

오르막 수 

1 초 256 MB 52583 25782 20014 47.782%

문제

오르막 수는 수의 자리가 오름차순을 이루는 수를 말한다. 이때, 인접한 수가 같아도 오름차순으로 친다.

예를 들어, 2234와 3678, 11119는 오르막 수이지만, 2232, 3676, 91111은 오르막 수가 아니다.

수의 길이 N이 주어졌을 때, 오르막 수의 개수를 구하는 프로그램을 작성하시오. 수는 0으로 시작할 수 있다.

입력

첫째 줄에 N (1 ≤ N ≤ 1,000)이 주어진다.

출력

첫째 줄에 길이가 N인 오르막 수의 개수를 10,007로 나눈 나머지를 출력한다.

예제 입력 1

1

예제 출력 1

10

예제 입력 2

2

예제 출력 2

55

예제 입력 3

3

예제 출력 3

220

 

동적 프로그래밍 기법을 통해 해결할 수 있는 문제이다.

 

문제를 해결하기 위한 아이디어는 다음과 같다.

우선 한자리 수를 상상해보자. 한자리 수의 경우 0,1,2...,9 까지의 수가 문제의 조건을 충족하므로 오르막 수의 개수는 10개이다.

두자리 수를 상상해보자. '오르막 수'가 되기 위해선 앞 자리수보다 크거나 같은 수만이 뒤에 자리수에 올 수 있다.

예를 들어 2 _ 인 경우 뒤에 자리에 올 수 있는 수의 개수는 2,3,4,..,9 로 8개가 될 것이다.

앞의 자리 수는 또한 0부터 9까지 가능하므로 앞의 자리 수가 어떤 수인지에 따라 뒷자리 수의 조건을 달리하여 적용시킬 수 있고,

이를 종합하면 해당 자리 수일때 가능한 모든 오르막 수를 알 수 있다.

 

이러한 조건을 이해하였다면 이를 dp테이블을 통해 나타내어 보자

'자리수'가 몇인지를 표현하기 위해 2차원 배열을 이용하여 dp[n]과 같이 표현하고 앞의 자리수가 몇인지에 대한 정보를 dp[n][0] 과 같이 표현한다.

예를 들어 2자리 자연수 일때, 앞의 자리수가 1인 상황은 dp[2][1]과 같이 표현 하도록 한다. 이는 코드를 편하게 짜기위해 내가 생각한 방법이므로 충분히 다른방식으로 표현하여도 괜찮다.

 

이를 그림으로 표현하면 다음과 같다.

 

위의 이미지에서 알 수 있듯이, 2자리수 오르막 수의 총합은 sum(dp[2])와 같이 표현이 가능하다.

또한 위의 과정을 통해 다음과 같은 점화식을 만들어 볼 수 있다.

 

dp[n][i] = sum( dp[n-1][i] + dp[n-1][i+1] + ... + dp[n-1][9])

 

위의 식을 해석해보면 다음과 같다. n자리 자연수에 대하여 앞자리 수가 i일때의 오르막 수의 개수는 앞자리수가 i~9 까지인 n-1자리수의 오르막 수의 합과 같다.

 

예를 들어 3자리 자연수가 있다고 해보자.

맨 앞자리 자연수로 가능한 수는 0부터 9까지가 될것이므로, 앞자리수가 0일때부터 차근차근 적어보자.

0 _ _ 의 경우 앞자리가 0인 3자리 오르막 수의 개수는 앞자리수가 (0~9)인 2자리 오르막 수의 개수와 같을 것이다.(오르막 수가 되기 위해선 앞자리 수보다 크면 안되기 때문이다.)

1 _ _ 의 경우 앞자리가 1인 3자리 오르막 수의 개수는 앞자리수가 (1~9)인 2자리 오르막 수의 개수와 같을 것이다.

(d[3][1] = sum(dp[2][1] + dp[2][2] + ... + dp[2][9] )

 

위의 과정을 반복하면 dp[3]에 dp[3][0] 부터 dp[3][9]까지의 데이터가 기록되고, dp[3]의 총합이 3자리수 오르막 수의 개수가 된다.

 

이를 코드로 나타내면 다음과 같다.

n = int(input())

# dp테이블 생성
# n자리수까지 고려
dp = [[0]*10 for _ in range(1001)]

# 1자리 수의 경우는 0~9로 10개이다.
dp[1] = 10
# 2자리 수의 경우는 앞자리 수에 따라 달라진다.
for i in range(10):  # 앞자리 수는 0~9가 될 수 있다.
    # 앞자리수가 i일때의 2자리 수의 오르막수의 개수는 다음과 같이 표현한다.
    dp[2][i] = (dp[1] - i)

# 3자리수 이상부터 알고리즘시작(2자리수까지는 이미 계산되어있으므로)
for k in range(3, n+1):  # n자리수까지 작동
    for i in range(10):  # 앞자리 수는 0~9가 될 수 있다.
        for j in range(i, 10):
            dp[k][i] += (dp[k-1][j] % 10007)  # 모듈러 연산시행하여 저장(오버플로우 방지)

# 정답 출력
if n == 1:
    print(10)
else:
    print(sum(dp[n]) % 10007)
 

위 코드에서 dp테이블에 값을 기록할때 오버플로우가 발생하지 않도록 하기 위해서는 모듈러 연산을 이용하여 수를 낮춰서 저장할 필요가 있는데, 문제에서 친절히 10007로 나눈 결과를 출력하라고 알려주었으므로 저장할때마다 10007으로 나눈값을 저장하고, 출력할때도 10007로 나눈 나머지를 출력하도록 한다.

 

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

 

11057번: 오르막 수

오르막 수는 수의 자리가 오름차순을 이루는 수를 말한다. 이때, 인접한 수가 같아도 오름차순으로 친다. 예를 들어, 2234와 3678, 11119는 오르막 수이지만, 2232, 3676, 91111은 오르막 수가 아니다. 수

www.acmicpc.net

 

 

728x90

스택

0.5 초 (추가 시간 없음) 256 MB 245023 88329 64207 37.338%

문제

정수를 저장하는 스택을 구현한 다음, 입력으로 주어지는 명령을 처리하는 프로그램을 작성하시오.

명령은 총 다섯 가지이다.

  • push X: 정수 X를 스택에 넣는 연산이다.
  • pop: 스택에서 가장 위에 있는 정수를 빼고, 그 수를 출력한다. 만약 스택에 들어있는 정수가 없는 경우에는 -1을 출력한다.
  • size: 스택에 들어있는 정수의 개수를 출력한다.
  • empty: 스택이 비어있으면 1, 아니면 0을 출력한다.
  • top: 스택의 가장 위에 있는 정수를 출력한다. 만약 스택에 들어있는 정수가 없는 경우에는 -1을 출력한다.

입력

첫째 줄에 주어지는 명령의 수 N (1 ≤ N ≤ 10,000)이 주어진다. 둘째 줄부터 N개의 줄에는 명령이 하나씩 주어진다. 주어지는 정수는 1보다 크거나 같고, 100,000보다 작거나 같다. 문제에 나와있지 않은 명령이 주어지는 경우는 없다.

출력

출력해야하는 명령이 주어질 때마다, 한 줄에 하나씩 출력한다.

예제 입력 1

14
push 1
push 2
top
size
empty
pop
pop
pop
size
empty
pop
push 3
empty
top

예제 출력 1

2
2
0
2
1
-1
0
1
-1
0
3

예제 입력 2

7
pop
top
push 123
top
pop
top
pop

예제 출력 2

-1
-1
123
123
-1
-1

 

스택에 대해 알고있는지, 자신의 언어로 스택을 구현하여 사용할 수 있는지를 묻는 문제이다.

새로운 언어를 연습할때 풀기에 좋은 문제라고 생각된다.

 

스택이 무엇인지 잘 모른다면 아래 게시글을 참고하길 바란다.

 

https://76codefactory.tistory.com/28

 

 

스택에 대해 알아보자

스택이 무엇인지 정확히 모르더라도, '스택을 쌓는다' 라는 말은 흔히들 들어 보았을 것이다. '스택(Stack)'은 데이터를 마치 동전을 쌓듯이, 위에서 위로 쌓아 올리는 자료구조이다. 쌓여있는 동

76codefactory.tistory.com

 

 

파이썬은 리스트가 배열과 스택의 역할을 동시에 하므로, 문제의 조건에 맞춰서 코드를 작성하면 되는 아주 간단한 문제이다.

주의해야 할점은 명령을 입력받는데 걸리는 시간을 단축하지 않으면 시간초과 판정을 받을 수 있으므로 수많은 데이터를 입력받을때는

sys의 stdin.readline을 통해 입력받아야 시간을 단축시킬 수 있다. 또한 언어를 선택할시 python3가 아닌 pypy3를 선택해야지 정답 판정을 받을 수 있다.

 

스택과 파이썬의 리스트에 대해 알고 있다면, 별도의 해설이 필요하지 않다고 판단하여 코드의 주석으로 설명을 대체하겠다.

# https://www.acmicpc.net/problem/10828

# 시간초과판정을 받지 않기 위하여(입력에 걸리는 시간을 단축하기 위해)
import sys
input = sys.stdin.readline

# 스택의 역할을 할 리스트 생성
stack = []

# 명령의 횟수
n = int(input())

# len 명령을 최대한 줄이기 위해 스택의 크기를 측정할 변수 생성
count = 0

for i in range(n):
    # 명령 입력받기
    command = list(map(str, input().split()))
    # push 명령이 존재한다면 숫자와 분할
    if command[0] == "push":
        stack.append(int(command[1]))
        count += 1  # 스택 내용물 추가
    elif command[0] == "top":
        # 스택이 비어있다면 -1 출력
        if count == 0:
            print(-1)
        else:
            # 가장 위에 있는 정수 출력
            print(stack[-1])
    elif command[0] == "size":
        # 스택의 크기를 출력한다.
        print(count)
    elif command[0] == "empty":
        # 스택이 비어있으면 1을 출력하고 아니면 0을 출력한다.
        if count == 0:
            print(1)
        else:
            print(0)
    elif command[0] == "pop":
        # 스택이 비어있다면
        if count == 0:
            print(-1)
        else:  # 스택이 비어있지 않다면
            print(stack.pop())
            count -= 1
 

 

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

 

728x90

토마토 

1 초 256 MB 177269 68852 43773 36.408%

문제

철수의 토마토 농장에서는 토마토를 보관하는 큰 창고를 가지고 있다. 토마토는 아래의 그림과 같이 격자 모양 상자의 칸에 하나씩 넣어서 창고에 보관한다.

창고에 보관되는 토마토들 중에는 잘 익은 것도 있지만, 아직 익지 않은 토마토들도 있을 수 있다. 보관 후 하루가 지나면, 익은 토마토들의 인접한 곳에 있는 익지 않은 토마토들은 익은 토마토의 영향을 받아 익게 된다. 하나의 토마토의 인접한 곳은 왼쪽, 오른쪽, 앞, 뒤 네 방향에 있는 토마토를 의미한다. 대각선 방향에 있는 토마토들에게는 영향을 주지 못하며, 토마토가 혼자 저절로 익는 경우는 없다고 가정한다. 철수는 창고에 보관된 토마토들이 며칠이 지나면 다 익게 되는지, 그 최소 일수를 알고 싶어 한다.

토마토를 창고에 보관하는 격자모양의 상자들의 크기와 익은 토마토들과 익지 않은 토마토들의 정보가 주어졌을 때, 며칠이 지나면 토마토들이 모두 익는지, 그 최소 일수를 구하는 프로그램을 작성하라. 단, 상자의 일부 칸에는 토마토가 들어있지 않을 수도 있다.

입력

첫 줄에는 상자의 크기를 나타내는 두 정수 M,N이 주어진다. M은 상자의 가로 칸의 수, N은 상자의 세로 칸의 수를 나타낸다. 단, 2 ≤ M,N ≤ 1,000 이다. 둘째 줄부터는 하나의 상자에 저장된 토마토들의 정보가 주어진다. 즉, 둘째 줄부터 N개의 줄에는 상자에 담긴 토마토의 정보가 주어진다. 하나의 줄에는 상자 가로줄에 들어있는 토마토의 상태가 M개의 정수로 주어진다. 정수 1은 익은 토마토, 정수 0은 익지 않은 토마토, 정수 -1은 토마토가 들어있지 않은 칸을 나타낸다.

토마토가 하나 이상 있는 경우만 입력으로 주어진다.

출력

여러분은 토마토가 모두 익을 때까지의 최소 날짜를 출력해야 한다. 만약, 저장될 때부터 모든 토마토가 익어있는 상태이면 0을 출력해야 하고, 토마토가 모두 익지는 못하는 상황이면 -1을 출력해야 한다.

예제 입력 1

6 4
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 1

예제 출력 1

8

예제 입력 2

6 4
0 -1 0 0 0 0
-1 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 1

예제 출력 2

-1

예제 입력 3

6 4
1 -1 0 0 0 0
0 -1 0 0 0 0
0 0 0 0 -1 0
0 0 0 0 -1 1

예제 출력 3

6

예제 입력 4

5 5
-1 1 0 0 0
0 -1 -1 -1 0
0 -1 -1 -1 0
0 -1 -1 -1 0
0 0 0 0 0

예제 출력 4

14

예제 입력 5

2 2
1 -1
-1 1

예제 출력 5

0

 

 

너비 우선 탐색(BFS : Breadth First Search)으로 풀 수 있는 문제이다.

 

BFS가 무엇인지 모른다면 아래 게시글을 참고하길 바란다.

토마토는 한 정점에서 상,하,좌,우 네 방향으로 뻗어나가서 익게 되는데 이는 그래프에서 인접정점을 방문하는것으로 해석이 가능하다.

 

문제를 해결하기 위한 아이디어는 다음과 같다.

정점의 값이 1인 토마토들을 우선적으로 BFS가 진행되어야 하고 인접정점의 값이 0일때만(익지 않은 토마토일때만) 인접 정점을 방문하도록 하여, 정점의 값을 +1씩 시켜나가며 탐색을 진행하고, 탐색이 끝난후 토마토들의 숫자중 가장 높은 숫자에서 -1을 한 값을 하면 최소 일수를 구할 수 있다.(탐색을 진행한 순번에 따라 수가 커지게 되므로)

또한 탐색이 종료된 이후, 값이 0인 정점이 존재한다면, 모든 토마토가 익지 못했다는 이야기가 되므로 문제의 조건에 맞춰 -1을 출력하면 된다.

 

코드를 보며 자세한 설명을 진행하도록 하겠다.

우선 내가 작성한 코드는 다음과 같다.

# 큐 자료구조를 쓰기 위해 덱을 import
from collections import deque

# 큐 생성
queue = deque()

# bfs를 수행하기위해 함수 정의

# 네 방향을 지정하기 위함
dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]

def bfs(graph):
    global m, n
    global queue
    # 큐에 값이 존재하는동안 탐색 진행
    while queue:
        node = queue.popleft()
        x, y = node[0], node[1]
        for i in range(4):
            # 상,하,좌,우에 대해 탐색 진행
            mx = x + dx[i]
            my = y + dy[i]
            if 0 <= mx < n and 0 <= my < m:  # 영역을 벗어나지 않는지 확인
                # 안익은 토마토만 익게하면 됨(안익은 토마토는 0)
                if graph[mx][my] == 0:
                    graph[mx][my] = graph[x][y] + 1  # 토마토를 익게함
                    queue.append([mx, my])


m, n = map(int, input().split())

graph = [list(map(int, input().split())) for _ in range(n)]

# 그래프 탐색을 진행하며 정수 1이 익은 토마토를 의미하므로,
# 하루가 지난후 익은 토마토의 영향을 받아 익은 토마토를 2로, 2 토마토에 영향을 받은 토마토를 3토마토로 증가하여 표현한다.
# 탐색 종료이후 그래프의 최대값을 출력하면, 최소일수와 관련있을것 => 단 2차원 그래프인점을 고려하여 출력
# # 탐색 종료 이후 그래프에 0인 토마토가 존재한다면 익지 않은 토마토가 있다는 의미이므로 -1을 출력하고, 그렇지 않다면 최소일수를 출력한다.

# 모든 정점을하나씩 방문하며 탐색 진행
for i in range(n):
    for j in range(m):
        if graph[i][j] == 1:
            # 탐색할 목록에 넣어두기 => 바로 bfs를 시행하지 않고, 날짜순으로 수행하기위해 담아만 둔다.
            queue.append([i, j])

# 1인 토마토에 대해 탐색 진행
bfs(graph)

# 안 익은 토마토(0)가 있는지 탐색하기 위함
isZero = False
minDate = 0  # 토마토가 다 익는데 걸리는 최소 일수를 기록하기 위함

# 안익은 토마토의 존재유무와 최소일수 계산
for g in graph:
    # 2차원 배열에서 한 줄 씩 검사를 실시한다 => 0인 토마토가 있는지
    if 0 in g:
        isZero = True
        break
    if minDate < max(g):
        # 최소일수를 구하기 위해
        minDate = max(g)

if isZero:
    print(-1)
else:
    # 모든 토마토가 다 익으면 최소일수를 출력한다.
    print(minDate-1)  # 마지막날짜에 +1 된 값이 들어가므로 -1을 해줌
 

BFS를 할것이므로 큐 자료구조를 사용할것이다. 따라서 큐의 역할을 할수 있는 덱 자료구조를 import하고 큐를 생성한다.

# 큐 자료구조를 쓰기 위해 덱을 import
from collections import deque

# 큐 생성
queue = deque()
 

 

문제의 조건에 따라 상,하,좌,우로 토마토가 익어가므로(이동할수 있으므로)

배열의 상,하,좌,우를 서로 간선으로 연결되어 있다고 생각한다.

 

방향을 표현하기 위한 배열로서 다음과 같이 리스트를 작성한다.

# 네 방향을 지정하기 위함
dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]
 

이를 각 좌표에 더하면 네방향으로 표현할 수 있다. 그림으로 표현하면 다음과 같다.

 

토마토 판의 크기와 현재 토마토의 상황을 입력받는다. 이후 배열의 모든 인덱스를 순환하며, 인덱스의 값이 1과 같다면, 큐에 삽입한다.

이렇게 삽입된 데이터를 바탕으로 BFS를 진행한다.

m, n = map(int, input().split())

graph = [list(map(int, input().split())) for _ in range(n)]

# 모든 정점을하나씩 방문하며 탐색 진행
for i in range(n):
    for j in range(m):
        if graph[i][j] == 1:
            # 탐색할 목록에 넣어두기 => 바로 bfs를 시행하지 않고, 날짜순으로 수행하기위해 담아만 둔다.
            queue.append([i, j])

# 1인 토마토에 대해 탐색 진행
bfs(graph)
 

BFS에 대한 코드는 다음과 같다.

네 방향으로 이동시 영역을 벗어나는지 확인해야 하므로 전역변수로 m,n을 이용하고, 큐에 데이터를 삽입해야 하므로 생성한 큐를 전역변수로 사용하기 위해 global 키워드를 이용한다.

큐에 데이터가 존재하는 동안 탐색을 진행하며, 큐에서 정점을 뽑아 상하좌우로 방문하고 해당 정점이 그래프의 영역(배열의 영역)을 벗어나지 않고 0이라면(아직 안 익은 토마토라면)방문한다.

def bfs(graph):
    global m, n
    global queue
    # 큐에 값이 존재하는동안 탐색 진행
    while queue:
        node = queue.popleft()
        x, y = node[0], node[1]
        for i in range(4):
            # 상,하,좌,우에 대해 탐색 진행
            mx = x + dx[i]
            my = y + dy[i]
            if 0 <= mx < n and 0 <= my < m:  # 영역을 벗어나지 않는지 확인
                # 안익은 토마토만 익게하면 됨(안익은 토마토는 0)
                if graph[mx][my] == 0:
                    graph[mx][my] = graph[x][y] + 1  # 토마토를 익게함
                    queue.append([mx, my])
 

배열의 값이 1인 정점들을 큐에 우선적으로 삽입함으로서, 하나의 정점에서 계속해서 탐색을 이어나가지 않고, 각 정점에서 순차적으로 값이 뻗어나갈수 있게 된다.

 

탐색이 종료된 이후, 배열의 각 정점에 대해서 0인 값이 존재하는지 확인하고 0인 값이 존재한다면 안 익은 토마토가 존재한다는것이므로 최소일수 계산을 중단한다.

0인 값이 존재하지 않는다면, 배열의 각 요소에 대해서 최대값을 계산하고, 출력할때는 -1을 한다.(1일차부터 2로 계산되므로)

# 안익은 토마토의 존재유무와 최소일수 계산
for g in graph:
    # 2차원 배열에서 한 줄 씩 검사를 실시한다 => 0인 토마토가 있는지
    if 0 in g:
        isZero = True
        break
    if minDate < max(g):
        # 최소일수를 구하기 위해
        minDate = max(g)

if isZero:
    print(-1)
else:
    # 모든 토마토가 다 익으면 최소일수를 출력한다.
    print(minDate-1)  # 마지막날짜에 +1 된 값이 들어가므로 -1을 해줌
 

+ Recent posts