Study

[묘공단]파이썬 스터디 12주차

A란 2024. 2. 24. 00:59

동적 계획법

전체문제를 한번에 해결하는 것이 아니라 작은 부분 문제들을 해결하고, 이것들을 활용하여 전체 문제를 해결하는 방법.

  • 점화식 세우기와 동적계획법
  1. 문제를 해결하는 해가 이미 있다고 가정.
  2. 종료 조건 설정.
  3. 과정 1,2를 활용해 점화식 세우기

점화식 구현: 재귀 활용

def fact(N):
    if N == 1:
        return 1
    else:
        return fact(N-1) * N

하지만, 스택 메모리에 함수 호출 정보가 많이 쌓이므로 메모리 한계로 런타임 오류가 발생할 수 있음. 따라서 재귀 호출을 해서 문제가 생겼거나, 생길 거 같을 때 취할 수 있는 방법은 다음과 같다.

- 재귀 호출 자체를 쓰지 않는 법 : 반복문

- 재귀 호출의 횟수를 줄이는 법 : 메모이제이션

 

  • 재귀 호출의 횟수를 줄여주는 메모이제이션

재귀 호출의 횟수를 줄이는 방법. 반복 연산 횟숫를 줄여 알고리즘의 성능을 올려줌.

점화식 구현: 재귀+메모이제이션 활용

1) 메모이제이션을 위한 저장소 생성 : 이미 구한 해를 저장할 공간을 생성.

2) 재귀 함수 정의 : 점화식을 재귀로 표현할 함수를 정의한다. 이때 함수의 세부 구현은 고려하지 않는다.

3) 재휘 함수의 종료조건을 정의 

4) 재귀 함수의 일반 연산 처리 

fibodata = [0] * (N + 1)

def fibo(N):
    if fibodata[N] != 0:
        return fibodata[N] # 메모이제이션 활용
    if N <= 2:
        fibodata[N] = 1 # 메모이제이션, 종료조건
    else:
        fibodata[N] = fibo(N-1) + fibo(N-2) #메모이제이션, 일반항
    return fibodata[N]

 

  • 최장 증가 부분 수열

부분 수열이란?

주어진 수열 중 일부를 뽑아 새로 만든 수열.

최장 증가 부분 수열이란?

부분 수열의 원소가 오름차순을 유지하면서도 길이가 가장 긴 수열.

 

LTS의 길이 동적 계획법으로 구하기

길이가 가장 긴 증가하는 부분 수열 찾기.

 

LTS의 특징

- 숫자가 점점 증가함.

- 원소 간의 전후 관계는 유지함.

 

  • 최장 공통 부분 수열( LCS )

특정 순서로 숫자를 나열하는 것. 두 수열이 어떤 기준에 따라 양쪽에서 공통으로 발견 할 수 있는 가장 긴 부분 수열을 의미.

 

LCS 길이 점화식 생각하기

- 두 문자열의 특정 문자가 같은지

- 같다면 찾은 두문자의 위치가 이전에 찾은 문자의 다음에 있는지

 

문제풀이

  • 가장 큰 정사각형 찾기

1 1 1   0 1 1 1
1 1 1   1 2 2 2
1 1 1   1 2 3 3
0 0 1 0   ....

처음 작성한 코드에서 난 에러 (case1 실패) :

1*1 정사각형을 고려해서 그 부분을 따로 처리해줘야함.  case1이 그런 경우인듯함.

더보기

처음 작성한 코드는 자꾸 case 1에서만 실패가 나옴.

def solution(board):
    answer = 0
    
    row_num = len(board)
    col_num = len(board[0])
    
    # 주어진 board를 돌면서 가장 큰 정사각형 찾기
    for i in range(1, row_num):
        for j in range(1, col_num):
            if board[i][j] == 1:
              left = board[i-1][j]
              left_up = board[i-1][j-1]
              up = board[i][j-1]
              # 주변의 왼쪽, 왼쪽위, 위 값 중 최솟값 + 1을 현재 값으로 갱신
              board[i][j] = min(left, left_up, up) + 1
              # print(type(board[i][j]))
              answer = max(answer, board[i][j])
            
    
    # 정사각형의 넓이 반환
    return answer ** 2
def solution(board):
    answer = 0
    
    row_num = len(board)
    col_num = len(board[0])
    
    if row_num == 1 and col_num == 1: # 1*1정사각형의 경우
        return board[0][0]
        
     # 주어진 board를 돌면서 가장 큰 정사각형 찾기
    for i in range(1, row_num):
        for j in range(1, col_num):
            if board[i][j] == 1:
                left = board[i-1][j]
                left_up = board[i-1][j-1]
                up = board[i][j-1]
                # 주변의 왼쪽, 왼쪽위, 위 값 중 최솟값 + 1을 현재 값으로 갱신
                board[i][j] = min(left, left_up, up) + 1
                answer = max(answer, board[i][j])
                
    return answer ** 2

 

초반에 if 문 사용안하고 answer를 현재 배열에서 가능한 가장 큰 정사각형의 크기로 설정하고, 나중에 갱신하는 방식

더보기
def solution(board):
    row_num = len(board)
    col_num = len(board[0])
    
    # 주어진 board를 돌면서 가장 큰 정사각형 찾기
    answer = max(map(int, board[0]))  # 첫 번째 행에서 가능한 가장 큰 정사각형 크기로 초기화
    
    for i in range(1, row_num):
        for j in range(1, col_num):
            if board[i][j] == 1:
                left = board[i-1][j]
                left_up = board[i-1][j-1]
                up = board[i][j-1]
                # 주변의 왼쪽, 왼쪽위, 위 값 중 최솟값 + 1을 현재 값으로 갱신
                board[i][j] = min(left, left_up, up) + 1
                answer = max(answer, board[i][j])
                
    return answer ** 2