Skip to content

Minjeong / 3월 4주차 / 4문제#8

Merged
Kojungbeom merged 8 commits intomainfrom
minjeong
Mar 24, 2024
Merged

Minjeong / 3월 4주차 / 4문제#8
Kojungbeom merged 8 commits intomainfrom
minjeong

Conversation

@Mingguriguri
Copy link
Collaborator

목표 문제 수: 4개

푼 문제

아래는 제가 정리한 내용 중에서, 핵심이 되는 플로우와 TIL 위주를 가져왔습니다.

백준 #1904. 01타일: DP 실버3

정리한 노션 링크: 노션
이걸 어떻게 문제를 풀까 고민하다가, DP는 “점화식”이 가장 중요하다는게 떠올랐다.

점화식은 예제에 나온 값들 간의 “관계성”과 “규칙성”을 찾아야 한다. 따라서 그 부분에 대해 집중해서 보았다. 마치 규칙이 피보나치 수열처럼 $A_i = A_{i-1} +A_{i-2}$ 처럼 되면 성립할 것처럼 보였다.

🚩플로우

  • n의 값을 입력받는다.
  • dp 테이블을 미리 정의해놓는다. 이때 0번 인덱스가 아닌 1번 인덱스부터 쓸 것이기 때문에 초기화할 때 배열의 크기를 신경써야 한다. 따라서 n+1이 아니라 n+2까지 여유있게 초기화한다.
  • 내가 찾은 규칙은 $A_i = A_{i-1} +A_{i-2}$ 이다. 따라서, 이 식을 for문안에 넣어준다. 또한, 문제의 조건에서 15746의 나머지값을 출력하라고 하였으므로 이 부분도 반영한다
 dp[i] = (dp[i-1] + dp[i-2])%15746

🚩My submission

import sys
input = sys.stdin.readline

n = int(input())

dp = [0] * (n+2)

dp[1] = 1
dp[2] = 2


for i in range(3, n+1):
    dp[i] = (dp[i-1] + dp[i-2])%15746

print(dp[n])

💡TIL

  • DP문제는 고민하고 생각하는 시간은 길지만 코드 작성하는 시간은 짧은 것 같다.
  • 나름 15746을 나누는 거 놓치지 않겠다고 집중해서 보았는데, 15746 나누는 부분이 문제였다. 출력되는 수가 너무 커서 나머지한 값을 출력하도록 하는 조건이 있을 때, 메모리에 저장하는지 아닌지에 대해서도 고려해야 한다는 것을 알게 되었다. DP의 경우 메모이제이션을 사용하기 때문에 메모리 초과가 되지 않도록 그때 그때 계산해서 저장해야 효율적이라는 것도 알게 되었다. 덕분에 앞으로 문제를 좀 더 신중하게 풀 수 있을 것 같다.

백준 #9461. 파도반수열 : DP, 실버3

정리한 노션 링크: 노션
내가 찾은 점화식은 $P(N) = P(N-3)+P(N-2)$ 이다. 점화식에서 최소로 필요한 값인 P(N-3)을 만족하기 위해서는 반복문을 시작할 때의 값은 4부터 시작해야 하며, 이를 위해서는 P(1)~P(3) 까지는 미리 값이 1로 초기화되어 있어야 한다는 것을 알 수 있다

🚩플로우

  1. 테스트케이스의 수, T를 입력받는다.
  2. dp테이블, dp를 초기화한다. 문제에서 최대값이 100이고, 사용하는 값은 1부터 시작하므로 101사이즈로, 그리고 미리 설정해두어야 하는 P(1)~P(3)의 값이 1이므로 [1] * 101로 초기화한다.
  3. T만큼 반복하며 테스트케이스 값, p_n을 입력받는다.
  4. dp를 시작하는 반복문을 넣는다. 이는 4부터 p_n까지 반복하며, 위에서 세운 점화식 dp[i] = dp[i-3]+dp[i-2] 를 수행한다.
  5. 입력한 테스트케이스의 결과값을 출력한다.

🚩My submission

import sys
input = sys.stdin.readline

t = int(input()) #테스트케이스의 수
dp = [1] * 101
for __ in range(t): # 테스트케이스 수만큼 반복
    p_n = int(input()) # 테스트케이스 입력
    for i in range(4, p_n+1): #4부터 시작
        dp[i] = dp[i-3] + dp[i-2] # 점화식
    print(dp[p_n])

백준 #1912. 연속합: DP, 실버2

정리한 노션 링크: 노션
실패한 직관:

  • dp의 값과 현재 값을 if문으로 케이스를 나눠 비교하고, 의미있는 값은 누적한 값으로, 아닐 경우 연속될 필요가 없으므로 -1로 저장한다.

성공한 직관:

  • max()함수를 이용하여 누적된 값과, 현재 값을 비교하여 더 큰 값을 dp테이블에 저장한다.

🚩플로우

새로운 로직으로 다시 짰다. 이때의 키 포인트는 “지금까지 누적한 값과 현재값 중에서 더 큰 값으로 dp에 저장”한다는 것이다.

💡 점화식: max(누적값, 현재값)

플로우는 아래와 같다. (위의 플로우와 바뀐 부분은 빨간색으로 표시했다.)

  1. 입력 및 초기화하기 (개수: n, 수열: nums리스트, dp테이블: dp)
  2. 현재 인덱스값을 기준으로,
    ”이전 dp값에 nums의 현재값을 더한 값인 누적값”과 “nums의 현재값” 중 더 큰 값을 dp에 저장한다.
  3. 위의 과정을 dp의 길이만큼 반복한다.
  4. dp테이블에서 가장 큰 값을 출력한다. 이때, nums리스트에 있는 값들이 음수인 경우, 처음 초기화한 값인 0이 제일 크다고 인식하므로 실제 값을 의미하는 1번째~마지막으로 범위를 설정히여 큰 값을 출력하도록 한다.

🚩My submission

import sys
input = sys.stdin.readline

n = int(input())
nums = list(map(int, input().strip().split()))
dp = [0] * (n+1) # dp테이블 1부터 시작할 예정이므로, n+1크기로 초기화

for i in range(1, len(dp)):
    # 점화식: max(누적값, 현재값)
    dp[i] += max(dp[i-1] + nums[i-1], nums[i-1]) # 지금까지 누적한 값과 현재값 중에서 더 큰 값으로 dp에 저장

print(max(dp[1:])) # nums가 모두 음수일 경우, 초기화할 때 사용한 0이 최댓값이 되므로 실제 데이터가 저장되는 1번째 인덱스부터 최댓값을 찾음

💡TIL

  • dp는 dp테이블을 만들어야 한다는 것이 아니라 memoization을 활용할 수 있어야 한다.
    • 현재 있는 리스트를 활용할 수 있다면 최대한 활용한다.
    • 또한 무턱대고 바로 저장하는 것이 아니라 무엇을 저장해야 할지를 충분히 고민해야 한다.
  • 초과연산이 발생하지 않도록 해야 한다.
  • dp에는 max()또는 min()이 유용하게 사용될 수 있으므로, 이를 사용하는 방안으로 고민해보도록 한다.

PGS 숫자의표현: 브루트포스, 레벨2

정리한 노션 링크: 노션

🚩플로우

  1. 1부터 n까지의 숫자가 연속해야 하므로 이에 대한 외부 for문을 하나 만들고, 실제로 n과 같아질 때까지 값을 더해주는 내부 for문을 만든다.
    • 내부 for문에서는 인덱스값인 j를 누적해서 sum에 더한다.
    • sum이 n과 같아진다면 answer에 값을 하나 더 하고, 초과할 경우 for문에서 나온다.
  2. 외부 for문에서는 그 다음 i로 넘어가게 된다.

🚩My submission

def solution(n):
    answer = 0
    
    for i in range(1, n+1):
        sum = 0
        for j in range(i, n+1):
            sum += j
            
            if sum == n:
                answer += 1
                break
            elif sum > n:
                break
                
    return answer

@Kojungbeom
Copy link
Collaborator

금주는 DP문제를 많이 푸셨군요. 잘하셨습니다. LGTM

@Kojungbeom Kojungbeom merged commit edacfdd into main Mar 24, 2024
@Mingguriguri
Copy link
Collaborator Author

LGTM을 처음 알게 되었네요!! 호홍 새롭게 배우네요 감사합니당~~

코드 리뷰의 최종 목표는 다른 엔지니어로부터 해당 변경을 적용해도 된다는 합의를 이끌어내는 것입니다. 합의한 엔지니어는 ‘좋아 보임(looks good to me)’이라는 뜻의 LGTM이라는 태그를 달아 의사를 표현합니다. 구글에서는 이 LGTM이 변경을 커밋하는 데 요구되는 필수 항목입니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants