알고리즘 스터디 1주차
목차
1. BOJ 1010번 다리 놓기
2. BOJ 2504 괄호의 값
3. PostOrder iteration으로 구현하기
1. BOJ 1010번 다리 놓기
서쪽에 N개의 사이트,동쪽의 M개의 사이트가 있는데, 이를 연결할 수 있는 경우의 수를 묻는 문제이다.
풀이과정
이는 잘 생각해보면 둘의 Combination, 즉 조합을 구하라는 문제와 같다는 것을 알 수 있다.
가장 먼저 생각해볼 수 있는 Combination을 구하는 식이다.
하지만 이 식의 경우는 불필요한 Factorial 연산 + 나누기 연산이 들어가므로 바람직하지 않다.
Combination은 공식이 다양하게 있는데, 난 이항계수를 이용하여 풀어보았다.
다들 많이 본적이 있는 식이다.
이를 이해하기 쉽게 표현하자면
코드로 표현해보면
DP[i][j] = DP[i-1][j-1] + DP[i-1][j]
다음과 같이 정의할 수 있다.
추가적으로 다음식을 사용하게 되면
r > N/2 인 경우를 굳이 구하지 않고 처리할 수 있다
이를 이용하여 Dynamic Progamming을 사용하여 풀면 간단하게 풀린다.
Code
# 2차원 배열선언
## [[0]*30]*30 <- 이렇게 선언하지마시오... 주의!
answer = [[0 for col in range(31)] for row in range(31)]
def combination(N, M):
# 가장자리 Case 1
if N == 1 or M == 0:
return 1
# 가장자리 Case 2
if M == 1:
return N
# 조합 공식
if M >= N/2:
M = N - M
# 이미 구한 값이라면 return
if answer[N][M] != 0:
return answer[N][M]
# DP Memoization
answer[N][M] = combination(N-1, M) + combination(N-1, M-1)
return answer[N][M]
for N, M in input_array:
print("M Combination N: ", combination(M, N))
BOJ 2504 괄호의 값
괄호로된 문자열이 주어진다.
올바른 문자열이 다음과 같이 정의된다고 한다.
다음의 조건에 맞게 값을 구하라는 문제이다.
풀이 과정
Input의 길이가 1~30이므로 시간에 구애받는 문제는 아닌것으로 보인다.
다양한 풀이가 존재할 수 있겠지만,
난 이 문제를 DP를 이용하여 풀어보았다.
DP 배열은 다음과 같이 정의할 수 있다.
DP[i][j] : i ~ j 까지에서의 괄호열의 값
그럼 점화식은 어떻게 구할 수 있을까?
우선 케이스를 생각해보자
케이스는 두가지로 생각해볼 수 있다.
1. 괄호에 크게 쌓여있는 경우
이 경우에는 괄호 안에 있는 값에 괄호의 종류에 따라 곱셈을 해주어야한다. Ex) [ A ]인 경우는 곱하기 3 or ( A )인 경우는 곱하기 22. 두개의 괄호가 붙어있는 경우
이 경우는 두개의 완성된 괄호가 나열되어있는 경우인데, 이 경우는 두 값을 더해주어야한다. Ex) (A)(B)
그럼 우리는 DP 식을 다음과 같이 구상해볼 수 있다.
1. 괄호에 크게 쌓여있는 경우
DP[start][end] = DP[start+1][end-1] * (2 또는 3)2. 두개의 괄호가 붙어있는 경우 - mid는 start와 end 사이 어떤 값
DP[start][[end] = DP[start][mid] + DP[mid+1][end]
이 식을 이용하여 DP배열을 채워보자
일단 우리는 작은 괄호 먼저 값을 구해야 다음 괄호의 값을 구할 수 있다.
그래서 가장 작은 괄호열 '()' 와 '[]' 의 경우를 구해야한다. 이 때 괄호열의 길이는 2이다.
그 다음으로 괄호열의 길이는 4이다. 그 다음은 6이다.이때, 괄호의 길이는 2의 배수라는 것을 알 수 있는데, 이를 활용하여 2의 배수만큼 괄호열의 길이를 늘려 DP 값을 구해야한다.
그럼 길이가 2일때 먼저 구해보자.
길이가 2인 경우, 점화식은 다음과 같이 이루어져 있는데, 이상한 점을 하나 발견할 수 있다.
ex) 인풋이 '[]'일 경우 DP[0][1] = DP[1][0] * 30~1 사이의 길이를 구하는데 1~0 사이의 값이 사용된다.
이 문제를 해결하기 위해 간단한 트릭을 사용하였다.
i > j 인 경우 DP배열의 값을 1로 초기화해었다. (이외의 경우는 0으로 초기화)
1로 초기화하면 길이가 2인 괄호열의 경우에도 문제없이 구할 수 있게 된다.
코드는 앞선 경우의 수를 나누어 작성하였다.
Case 1. [A]인 경우
DP[start][end] = DP[start+1][end-1] * 3Case 2. (A)인 경우
DP[start][end] = DP[start+1][end-1] * 2Case 3. (A)(B)
DP[start][end] = DP[start][mid] + DP[mid+1][end]
Case 3의 경우는 start와 end 사이를 탐색하여 올바른 괄호열로 이루어진 경우에만 수행한다.
Code
배열 초기화
DP = [[1 if i > j else 0 for col in range(31)] for row in range(31)]
# DP = [[0 for col in range(31)] for row in range(31)]
#
# for i in range(31):
# for j in range(31):
# if i > j:
# DP[i][j] = 1
괄호열의 값 구하기
str = "(()[[]])([])" # 예시 Input
length = len(str)
for gap in range(2, length+1, 2):
for start in range(length-gap+1):
end = start + gap -1
if str[start] == '(' and str[end] == ')':
DP[start][end] = DP[start+1][end-1] * 2
elif str[start] == '[' and str[end] == ']':
DP[start][end] = DP[start+1][end-1] * 3
for i in range(start+1, end):
if DP[start][i] > 0 and DP[i+1][end] > 0:
DP[start][end] = max(DP[start][end], DP[start][i] + DP[i+1][end])
값 확인
print(DP[0][length-1])
Postorder Iteration 구현하기
친구의 부탁으로 트리탐색하는 방법 중 하나인 PostOrder를 Iterative하게 구현해보았다.
PostOrder는 재귀로 구현하면 쉽게 짤 수 있다.
def postorder(root):
postorder(root.left)
postorder(root.right)
print(root.value)
이를 Iterative하게 짜려면 stack을 사용해야한다.
사실 stack을 C, C++로만 사용해서 Python으론 사용해본 적이 처음인데, 기본 자료구조 list만으로도 구현이 가능하였다
어떻게 구현할지 생각해보자..
값을 출력하는 경우를 생각해보면,
1. Leaf 노드인 경우 2. 자식 노드들을 모두 탐색한 경우이 이외의 경우 외엔 자식노드를 먼저 탐색해야한다.
이때 우선 왼쪽 자식을 먼저 탐색한다.
그럼 stack을 이용해서 DFS를 하는 것이 좋겠다.
이때 stack이 어떻게 변화하는지에 대해서 잘 추적해야한다.
- Leaf 노드라면 출력한다.
- 자식 노드가 존재하고 탐색하지 않은 자식노드가 있다면 부모노드를 stack에 넣고 자식 노드로 이동한다.
- 왼쪽 오른쪽 모두 탐색했다면 (visited==True) 노드를 출력하고 stack을 pop하여 상위 노드로 올라간다.
코드로 보면 더 이해가 쉬울 것이다.
Code
def postorder_iter(root):
stack = [root]
while stack:
current = stack.pop()
# If current is leaf node
if current.left is None and current.right is None:
print(current.value)
current.visited = True
continue
# If left-child exists and not visitied
if current.left not None and current.left.visited is False:
stack.append(current)
stack.append(current.left)
# If right-child exists and not visitied
elif current.right is not None and current.right.visited is False:
stack.append(current)
stack.append(current.right)
else:
print(current.value)
current.visited = True
Author And Source
이 문제에 관하여(알고리즘 스터디 1주차), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@douvlecircle/알고리즘-스터디-1주차저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)