관리 메뉴

πŸ’»πŸ’­πŸŽ§πŸŒ

λ‹€μ΄λ‚˜λ―Ή ν”„λ‘œκ·Έλž˜λ° (Dynamic Programming) λ³Έλ¬Έ

μ•Œκ³ λ¦¬μ¦˜

λ‹€μ΄λ‚˜λ―Ή ν”„λ‘œκ·Έλž˜λ° (Dynamic Programming)

adorableco 2023. 1. 23. 22:01
λ°˜μ‘ν˜•

λ‹€μ΄λ‚˜λ―Ή ν”„λ‘œκ·Έλž˜λ°

  • λ‹€μ΄λ‚˜λ―Ή ν”„λ‘œκ·Έλž˜λ°μ€ λ©”λͺ¨λ¦¬λ₯Ό 적절히 μ‚¬μš©ν•˜μ—¬ μˆ˜ν–‰ μ‹œκ°„ νš¨μœ¨μ„±μ„ λΉ„μ•½μ μœΌλ‘œ ν–₯μƒμ‹œν‚€λŠ” 방법이닀.
  • 이미 κ³„μ‚°λœ κ²°κ³Ό(μž‘μ€ 문제)λŠ” λ³„λ„μ˜ λ©”λͺ¨λ¦¬ μ˜μ—­μ— μ €μž₯ν•˜μ—¬ λ‹€μ‹œ κ³„μ‚°ν•˜μ§€ μ•Šλ„λ‘ ν•œλ‹€.
  • λ‹€μ΄λ‚˜λ―Ή ν”„λ‘œκ·Έλž˜λ°μ˜ κ΅¬ν˜„μ€ 일반적으둜 두 가지  λ°©μ‹μœΌλ‘œ κ΅¬μ„±λœλ‹€.
    • Top-down (ν•˜ν–₯식)
    • Botton-up (상ν–₯식)
  • λ‹€μ΄λ‚˜λ―Ή ν”„λ‘œκ·Έλž˜λ°μ€ 동적 κ³„νšλ²•μ΄λΌκ³ λ„ λΆˆλ¦°λ‹€.

 

  • 일반적인 ν”„λ‘œκ·Έλž˜λ° λΆ„μ•Όμ—μ„œμ˜ 동적(Dynamic) μ˜λ―Έμ™€ 차이점
    • μžλ£Œκ΅¬μ‘°μ—μ„œ 동적 ν• λ‹Ή(Dynamic Allocation)은 'ν”„λ‘œκ·Έλž¨μ΄ μ‹€ν–‰λ˜λŠ” 도쀑에 싀행에 ν•„μš”ν•œ λ©”λͺ¨λ¦¬λ₯Ό ν• λ‹Ήν•˜λŠ” 기법'
    • λ‹€μ΄λ‚˜λ―Ή ν”„λ‘œκ·Έλž˜λ°μ—μ„œ 'λ‹€μ΄λ‚˜λ―Ή'은 별닀λ₯Έ μ˜λ―Έκ°€ μ—†μŒ 

<λ‹€μ΄λ‚˜λ―Ή ν”„λ‘œκ·Έλž˜λ°μ„ μ‚¬μš©ν•  수 μžˆλŠ” 졜적 쑰건>

 

- 졜적 λΆ€λΆ„ ꡬ쑰 (Optimal Substructure) : 큰 문제λ₯Ό μž‘μ€ 문제둜 λ‚˜λˆŒ 수 있으며 μž‘μ€ 문제의 닡을 λͺ¨μ•„μ„œ 큰 문제λ₯Ό ν•΄κ²°ν•  수 μžˆλ‹€.

- μ€‘λ³΅λ˜λŠ” λΆ€λΆ„ 문제 (Overlapping Subproblem) : λ™μΌν•œ μž‘μ€ 문제λ₯Ό 반볡적으둜 ν•΄κ²°ν•΄μ•Ό ν•œλ‹€. 

 

 

ν”Όλ³΄λ‚˜μΉ˜ μˆ˜μ—΄μ€ λ‹€μ΄λ‚˜λ―Ή ν”„λ‘œκ·Έλž˜λ°μœΌλ‘œ 효과적으둜 계산할 수 있음

  • ν”„λ‘œκ·Έλž˜λ°μ—μ„œλŠ” μ΄λŸ¬ν•œ μˆ˜μ—΄μ„ λ°°μ—΄μ΄λ‚˜ 리슀트λ₯Ό μ΄μš©ν•΄ ν‘œν˜„ν•œλ‹€.

 

# ν”Όλ³΄λ‚˜μΉ˜ ν•¨μˆ˜(Fibonacci Function)을 μž¬κ·€ν•¨μˆ˜λ‘œ κ΅¬ν˜„
def fibo(x):
	if x==1 or x==2:
    	return 1
    return fibo(x-1) + fibo(x-2)
    
print(fibo(4))

β€» λ‹¨μˆœ μž¬κ·€ ν•¨μˆ˜λ‘œ ν”Όλ³΄λ‚˜μΉ˜ μˆ˜μ—΄μ„ ν•΄κ²°ν•˜λ©΄ μ§€μˆ˜ μ‹œκ°„ λ³΅μž‘λ„λ₯Ό κ°€μ§€κ²Œ λœλ‹€. -> μ‹œκ°„ λ³΅μž‘λ„κ°€ 높아짐 : O(2^n)

 

 

λ©”λͺ¨μ΄μ œμ΄μ…˜ (Memoization) -> νƒ‘λ‹€μš΄ 방식

  • λ©”λͺ¨μ΄μ œμ΄μ…˜μ€ λ‹€μ΄λ‚˜λ―Ή ν”„λ‘œκ·Έλž˜λ°μ„ κ΅¬ν˜„ν•˜λŠ” 방법 쀑 ν•˜λ‚˜μ΄λ‹€.
  • ν•œ 번 κ³„μ‚°ν•œ κ²°κ³Όλ₯Ό λ©”λͺ¨λ¦¬ 곡간에 λ©”λͺ¨ν•˜λŠ” 기법.
    • 같은 문제λ₯Ό λ‹€μ‹œ ν˜ΈμΆœν•˜λ©΄ λ©”λͺ¨ν–ˆλ˜ κ²°κ³Όλ₯Ό κ·ΈλŒ€λ‘œ κ°€μ Έμ˜¨λ‹€.
    • 값을 기둝해 λ†“λŠ”λ‹€λŠ” μ μ—μ„œ 캐싱(Caching)이라고도 ν•œλ‹€.
  • μ—„λ°€νžˆ λ§ν•˜λ©΄ λ©”λͺ¨μ΄μ œμ΄μ…˜μ€ 이전에 κ³„μ‚°λœ κ²°κ³Όλ₯Ό μΌμ‹œμ μœΌλ‘œ 기둝해 λ†“λŠ” 넓은 κ°œλ…μ„ μ˜λ―Έν•œλ‹€.
    • ν•œ 번 κ³„μ‚°λœ κ²°κ³Όλ₯Ό λ‹΄μ•„ λ†“κΈ°λ§Œ ν•˜κ³  λ‹€μ΄λ‚˜λ―Ή ν”„λ‘œκ·Έλž˜λ°μ„ μœ„ν•΄ ν™œμš©ν•˜μ§€ μ•Šμ„ μˆ˜λ„ μžˆλ‹€.

 

β˜… λ‹€μ΄λ‚˜λ―Ή ν”„λ‘œκ·Έλž˜λ°μ˜ μ „ν˜•μ μΈ ν˜•νƒœλŠ” 보텀업 방식

- κ²°κ³Ό μ €μž₯용 λ¦¬μŠ€νŠΈλŠ” DP ν…Œμ΄λΈ”μ΄λΌκ³  뢀름

- 보텀업 λ°©μ‹μ—μ„œλŠ” 보톡 λ°˜λ³΅λ¬Έμ„ 씀

 

 

↓ ν”Όλ³΄λ‚˜μΉ˜ μˆ˜μ—΄ : νƒ‘λ‹€μš΄ λ‹€μ΄λ‚˜λ―Ή ν”„λ‘œκ·Έλž˜λ° μ†ŒμŠ€μ½”λ“œ

# ν•œ 번 κ³„μ‚°λœ κ²°κ³Όλ₯Ό λ©”λͺ¨μ΄μ œμ΄μ…˜(Memoization)ν•˜κΈ°  μœ„ν•œ 리슀트 μ΄ˆκΈ°ν™”
d = [0] * 100

# ν”Όλ³΄λ‚˜μΉ˜ ν•¨μˆ˜(Fibonacci Function)λ₯Ό μž¬κ·€ν•¨μˆ˜λ‘œ κ΅¬ν˜„(νƒ‘λ‹€μš΄ λ‹€μ΄λ‚˜λ―Ή ν”„λ‘œκ·Έλž˜λ°)
def fibo(x):
	# μ’…λ£Œ 쑰건(1 ν˜Ήμ€ 2일 λ•Œ 1을 λ°˜ν™˜)
    if x==1 or x==2:
    	return 1
    # 이미 κ³„μ‚°ν•œ 적 μžˆλŠ” 문제라면 κ·ΈλŒ€λ‘œ λ°˜ν™˜
    if d[x]!=0:
    	return d[x]
    # 아직 κ³„μ‚°ν•˜μ§€ μ•Šμ€ 문제라면 점화식에 λ”°λΌμ„œ ν”Όλ³΄λ‚˜μΉ˜ κ²°κ³Ό λ°˜ν™˜
    d[x] = fibo(x-1) + fibo(x-2)
    return d[x]

print(fibo(99))

 

↓ ν”Όλ³΄λ‚˜μΉ˜ μˆ˜μ—΄ : λ³΄ν…€μ—… λ‹€μ΄λ‚˜λ―Ή ν”„λ‘œκ·Έλž˜λ° μ†ŒμŠ€μ½”λ“œ

# μ•žμ„œ κ³„μ‚°λœ κ²°κ³Όλ₯Ό μ €μž₯ν•˜κΈ° μœ„ν•œ DP ν…Œμ΄λΈ” μ΄ˆκΈ°ν™”
d = [0]*100

# 첫 번째 ν”Όλ³΄λ‚˜μΉ˜ μˆ˜μ™€ 두 번째 ν”Όλ³΄λ‚˜μΉ˜ μˆ˜λŠ” 1
d[1] = 1
d[2] = 1
n = 99

# ν”Όλ³΄λ‚˜μΉ˜ ν•¨μˆ˜(Fibonacci Function) 반볡문으둜 κ΅¬ν˜„(보텀업 λ‹€μ΄λ‚˜λ―Ή ν”„λ‘œκ·Έλž˜λ°)
for i in range(3,n+1):
	d[i] = d[i-1] + d[i-2]

print(d[n])

→ μž‘μ€ 것듀뢀터 ν•΄κ²°ν•œ 후에 그것듀을 μ’…ν•©ν•΄μ„œ 큰 것을 해결함.

 

 

β˜… λ©”λͺ¨μ΄μ œμ΄μ…˜μ„ μ΄μš©ν•˜λŠ” 경우 ν”Όλ³΄λ‚˜μΉ˜ μˆ˜μ—΄ ν•¨μˆ˜μ˜ μ‹œκ°„ λ³΅μž‘λ„λŠ” O(N) 이닀.

→μ§€μˆ˜ μ‹œκ°„ λ³΅μž‘λ„μ—μ„œ μ„ ν˜• μ‹œκ°„ λ³΅μž‘λ„λ‘œ 쀄어듦

 

<λ‹€μ΄λ‚˜λ―Ή ν”„λ‘œκ·Έλž˜λ° λ¬Έμ œμ— μ ‘κ·Όν•˜λŠ” 방법>

  • 주어진 λ¬Έμ œκ°€ λ‹€μ΄λ‚˜λ―Ή ν”„λ‘œκ·Έλž˜λ° μœ ν˜•μž„μ„ νŒŒμ•…ν•˜λŠ” 것이 μ€‘μš”ν•˜λ‹€.
  • κ°€μž₯ λ¨Όμ € 그리디, κ΅¬ν˜„, μ™„μ „ 탐색 λ“±μ˜ μ•„μ΄λ””μ–΄λ‘œ 문제λ₯Ό ν•΄κ²° ν•  수 μžˆλŠ”μ§€ κ²€ν† ν•  수 μžˆλ‹€.
    • λ‹€λ₯Έ μ•Œκ³ λ¦¬μ¦˜μœΌλ‘œ 풀이 방법이 λ– μ˜€λ₯΄μ§€ μ•ŠμœΌλ©΄ λ‹€μ΄λ‚˜λ―Ή ν”„λ‘œκ·Έλž˜λ°μ„ κ³ λ €ν•΄λ³΄μž.
    • 특히, λΆ€λΆ„ λ¬Έμ œλ“€μ΄ μ€‘λ³΅λœλ‹€λ©΄ DPλ₯Ό μ μš©ν•΄λ³Ό 수 있음 (λΆ€λΆ„ λ¬Έμ œλ“€μ΄ μ€‘λ³΅λ˜μ§€ μ•Šμ„ 땐 λΆ„ν•  정볡 κ³ λ €)
  • 일단 μž¬κ·€ ν•¨μˆ˜λ‘œ λΉ„νš¨μœ¨μ μΈ μ™„μ „ 탐색 ν”„λ‘œκ·Έλž¨μ„ μž‘μ„±ν•œ 뒀에 (νƒ‘λ‹€μš΄) μž‘μ€ λ¬Έμ œμ—μ„œ κ΅¬ν•œ 닡이 큰 λ¬Έμ œμ—μ„œ κ·ΈλŒ€λ‘œ μ‚¬μš©λ  수 있으면, μ½”λ“œλ₯Ό κ°œμ„ ν•˜λŠ” 방법을 μ‚¬μš©ν•  수 μžˆλ‹€.

 


 

<문제1> 개미 전사

  • 개미 μ „μ‚¬λŠ” λΆ€μ‘±ν•œ μ‹λŸ‰μ„ μΆ©λ‹Ήν•˜κ³ μž λ©”λšœκΈ° λ§ˆμ„μ˜ μ‹λŸ‰μ°½κ³ λ₯Ό λͺ°λž˜ κ³΅κ²©ν•˜λ €κ³  ν•œλ‹€. λ©”λšœκΈ° λ§ˆμ„μ— λŠ” μ—¬λŸ¬ 개의 μ‹λŸ‰μ°½κ³ κ°€ μžˆλŠ”λ° μ‹λŸ‰μ°½κ³ λŠ” μΌμ§μ„ μœΌλ‘œ 이어져 μžˆλ‹€.
  • 각 μ‹λŸ‰μ°½κ³ μ—λŠ” 정해진 수의 μ‹λŸ‰μ„ μ €μž₯ν•˜κ³  있으며 개미 μ „μ‚¬λŠ” μ‹λŸ‰μ°½κ³ λ₯Ό μ„ νƒμ μœΌλ‘œ μ•½νƒˆν•˜μ—¬ μ‹λŸ‰ 을 빼앗을 μ˜ˆμ •μ΄λ‹€. μ΄λ•Œ λ©”λšœκΈ° 정찰병듀은 일직선상에 μ‘΄μž¬ν•˜λŠ” μ‹λŸ‰μ°½κ³  μ€‘μ—μ„œ μ„œλ‘œ μΈμ ‘ν•œ μ‹λŸ‰μ°½κ³ κ°€ κ³΅κ²©λ°›μœΌλ©΄ λ°”λ‘œ μ•Œμ•„μ±Œ 수 μžˆλ‹€.
  • λ”°λΌμ„œ 개미 전사가 μ •μ°°λ³‘μ—κ²Œ 듀킀지 μ•Šκ³  μ‹λŸ‰μ°½κ³ λ₯Ό μ•½νƒˆν•˜κΈ° μœ„ν•΄μ„œλŠ” μ΅œμ†Œν•œ ν•œ μΉΈ 이상 떨어진 μ‹λŸ‰μ°½κ³ λ₯Ό μ•½νƒˆν•΄μ•Ό ν•œλ‹€.
  • μ΄λ•Œ 개미 μ „μ‚¬λŠ” 두 번째 μ‹λŸ‰μ°½κ³ μ™€ λ„€ 번째 μ‹λŸ‰μ°½κ³ λ₯Ό μ„ νƒν–ˆμ„ λ•Œ μ΅œλŒ“κ°’μΈ 총 8개의 μ‹λŸ‰μ„ 빼앗을 수 μžˆλ‹€. 개미 μ „μ‚¬λŠ” μ‹λŸ‰μ°½κ³ κ°€ μ΄λ ‡κ²Œ 일직선상일 λ•Œ μ΅œλŒ€ν•œ λ§Žμ€ μ‹λŸ‰μ„ μ–»κΈ°λ₯Ό μ›ν•œλ‹€.
  • 개미 전사λ₯Ό μœ„ν•΄ μ‹λŸ‰μ°½κ³  Nκ°œμ— λŒ€ν•œ 정보가 μ£Όμ–΄μ‘Œμ„ λ•Œ 얻을 수 μžˆλŠ” μ‹λŸ‰μ˜ μ΅œλŒ“κ°’μ„ κ΅¬ν•˜λŠ” ν”„λ‘œκ·Έλž¨μ„ μž‘μ„±ν•˜λΌ.

<문제 ν•΄κ²°>

ai = i번째 μ‹λŸ‰μ°½κ³ κΉŒμ§€μ˜ 졜적의 ν•΄ (얻을 수 μžˆλŠ” μ‹λŸ‰μ˜ μ΅œλŒ“κ°’)

ki = i번째 μ‹λŸ‰μ°½κ³ μ— μžˆλŠ” μ‹λŸ‰μ˜ μ–‘

점화식 : ai = max(ai-1,ai-2+ki)

 

<μ½”λ“œ>

# μ •μˆ˜ N을 μž…λ ₯ λ°›κΈ°
n = int(input())
# λͺ¨λ“  μ‹λŸ‰ 정보 μž…λ ₯ λ°›κΈ°
array = list(map(int, input().split()))

# μ•žμ„œ κ³„μ‚°λœ κ²°κ³Όλ₯Ό μ €μž₯ν•˜κΈ° μœ„ν•œ DP ν…Œμ΄λΈ” μ΄ˆκΈ°ν™”
d = [0] * 100

# λ‹€μ΄λ‚˜λ―Ή ν”„λ‘œκ·Έλž˜λ° 진행 (보텀업)
d[0] = array[0]
d[1] = max(array[0], array[1])
for i in range(2,n):
    d[i] = max(d[i-1],d[i-2]+array[i])

# κ³„μ‚°λœ κ²°κ³Ό 좜λ ₯
print(d[n-1])

 

 

<문제2> 1둜 λ§Œλ“€κΈ°

  • μ •μˆ˜ Xκ°€ μ£Όμ–΄μ‘Œμ„ λ•Œ, μ •μˆ˜ X에 μ‚¬μš©ν•  수 μžˆλŠ” 연산은 λ‹€μŒκ³Ό 같이 4가지이닀.
  1. Xκ°€ 5둜 λ‚˜λˆ„μ–΄ 떨어지면, 5둜 λ‚˜λˆˆλ‹€.
  2. Xκ°€ 3으둜 λ‚˜λˆ„μ–΄ 떨어지면, 3으둜 λ‚˜λˆˆλ‹€.
  3. Xκ°€ 2둜 λ‚˜λˆ„μ–΄ 떨어지면, 2둜 λ‚˜λˆˆλ‹€.
  4. Xμ—μ„œ 1을 λΊ€λ‹€.
  • μ •μˆ˜ Xκ°€ μ£Όμ–΄μ‘Œμ„ λ•Œ, μ—°μ‚° 4개λ₯Ό 적절히 μ‚¬μš©ν•΄μ„œ 값을 1둜 λ§Œλ“€κ³ μž ν•œλ‹€. 연산을 μ‚¬μš©ν•˜λŠ” 횟수의 μ΅œμ†Ÿκ°’μ„ 좜λ ₯ν•˜λΌ. 예λ₯Ό λ“€μ–΄ μ •μˆ˜κ°€ 26이면 λ‹€μŒκ³Ό 같이 κ³„μ‚°ν•΄μ„œ 3번의 연산이 μ΅œμ†Ÿκ°’μ΄λ‹€.
    • 26 → 25 → 5 → 1

 

<μ½”λ“œ>

# μ •μˆ˜ Xλ₯Ό μž…λ ₯ λ°›κΈ°
x = int(input())

# μ•žμ„œ κ³„μ‚°λœ κ²°κ³Όλ₯Ό μ €μž₯ν•˜κΈ° μœ„ν•œ DP ν…Œμ΄λΈ” μ΄ˆκΈ°ν™”
d = [0] * 30001

# λ‹€μ΄λ‚˜λ―Ή ν”„λ‘œκ·Έλž˜λ° 진행 (보텀업)
for i in range(2, x+1):
    # ν˜„μž¬μ˜ μˆ˜μ—μ„œ 1을 λΉΌλŠ” 경우
    d[i] = d[i-1] + 1
    # ν˜„μž¬μ˜ μˆ˜κ°€ 2둜 λ‚˜λˆ„μ–΄ λ–¨μ–΄μ§€λŠ” 경우
    if i%2==0:
        d[i] = min(d[i],d[i//2]+1)
    # ν˜„μž¬μ˜ μˆ˜κ°€ 3으둜 λ‚˜λˆ„μ–΄ λ–¨μ–΄μ§€λŠ” 경우
    if i%3==0:
        d[i] = min(d[i],d[i//3]+1)
    # ν˜„μž¬μ˜ μˆ˜κ°€ 5둜 λ‚˜λˆ„μ–΄ λ–¨μ–΄μ§€λŠ” 경우
    if i%5==0:
        d[i] = min(d[i],d[i//5]+1)

print(d[x])

 

 

 

<문제3> 효율적인 화폐 ꡬ성

  • N가지 μ’…λ₯˜μ˜ 화폐가 μžˆλ‹€. 이 ν™”νλ“€μ˜ 개수λ₯Ό μ΅œμ†Œν•œμœΌλ‘œ μ΄μš©ν•΄μ„œ κ·Έ κ°€μΉ˜μ˜ 합이 M원이 λ˜λ„λ‘ ν•˜λ €κ³  ν•œλ‹€. μ΄λ•Œ 각 μ’…λ₯˜μ˜ ν™”νλŠ” λͺ‡ κ°œλΌλ„ μ‚¬μš©ν•  수 μžˆλ‹€.
  • 예λ₯Ό λ“€μ–΄ 2원, 3원 λ‹¨μœ„μ˜ 화폐가 μžˆμ„ λ•ŒλŠ” 15원을 λ§Œλ“€κΈ° μœ„ν•΄ 3원을 5개 μ‚¬μš©ν•˜λŠ” 것이 κ°€μž₯ μ΅œμ†Œν•œμ˜ 화폐 κ°œμˆ˜μ΄λ‹€.
  • M원을 λ§Œλ“€κΈ° μœ„ν•œ μ΅œμ†Œν•œμ˜ 화폐 개수λ₯Ό 좜λ ₯ν•˜λŠ” ν”„λ‘œκ·Έλž¨μ„ μž‘μ„±ν•˜λΌ.

 

<μ½”λ“œ>

# μ •μˆ˜ N,M을 μž…λ ₯λ°›κΈ°
n,m = map(int, input().split())
# N개의 ν™”νŽ˜ λ‹¨μœ„ 정보λ₯Ό μž…λ ₯λ°›κΈ°
array = []
for i in range(n):
    array.append(int(input()))

# ν•œ 번 κ³„μ‚°λœ κ²°κ³Όλ₯Ό μ €μž₯ν•˜κΈ° μœ„ν•œ DP ν…Œμ΄λΈ” μ΄ˆκΈ°ν™”
d = [10001] * (m+1)

# λ‹€μ΄λ‚˜λ―Ή ν”„λ‘œκ·Έλž˜λ° 진행 (보텀업)
d[0] = 0
for i in range(n):
    for j in range(array[i],m+1):
        if d[j-array[i]] != 10001:
            d[j] = min(d[j], d[j-array[i]]+1)

# κ³„μ‚°λœ κ²°κ³Ό 좜λ ₯
if d[m] == 10001: # μ΅œμ’…μ μœΌλ‘œ M원을 λ§Œλ“œλŠ” 방법이 μ—†λŠ” 경우
    print(-1)
else:
    print(d[m])

 

 

 

 

<문제3> κΈˆκ΄‘

  • n x m 크기의 κΈˆκ΄‘μ΄ μžˆλ‹€. κΈˆκ΄‘μ€ 1 x 1 크기의 칸으둜 λ‚˜λˆ„μ–΄μ Έ 있으며, 각 칸은 νŠΉμ •ν•œ 크기의 금이 λ“€μ–΄ μžˆλ‹€.
  • μ±„κ΅΄μžλŠ” 첫 번째 μ—΄λΆ€ν„° μΆœλ°œν•˜μ—¬ κΈˆμ„ 캐기 μ‹œμž‘ν•œλ‹€. 맨 μ²˜μŒμ—λŠ” 첫 번째 μ—΄μ˜ μ–΄λŠ ν–‰μ—μ„œλ“  μΆœλ°œν•  수 μžˆλ‹€. 이후에 m - 1λ²ˆμ— κ±Έμ³μ„œ 맀번 였λ₯Έμͺ½ μœ„, 였λ₯Έμͺ½, 였λ₯Έμͺ½ μ•„λž˜ 3가지 쀑 ν•˜λ‚˜μ˜ μœ„μΉ˜λ‘œ 이동해야 ν•œλ‹€. 결과적으둜 μ±„κ΅΄μžκ°€ 얻을 수 μžˆλŠ” 금의 μ΅œλŒ€ 크기λ₯Ό 좜λ ₯ν•˜λŠ” ν”„λ‘œκ·Έλž¨μ„ μž‘μ„±ν•˜λΌ.

<문제 ν•΄κ²°>

  • κΈˆκ΄‘μ˜ λͺ¨λ“  μœ„μΉ˜μ— λŒ€ν•˜μ—¬ λ‹€μŒμ˜ μ„Έ κ°€μ§€λ§Œ κ³ λ €ν•˜λ©΄ λœλ‹€.
    • μ™Όμͺ½ μœ„μ—μ„œ μ˜€λŠ” 경우
    • μ™Όμͺ½ μ•„λž˜μ—μ„œ μ˜€λŠ” 경우
    • μ™Όμͺ½μ—μ„œ μ˜€λŠ” 경우
  • μ„Έ 가지 경우 μ€‘μ—μ„œ κ°€μž₯ λ§Žμ€ κΈˆμ„ 가지고 μžˆλŠ” 경우λ₯Ό ν…Œμ΄λΈ”μ— κ°±μ‹ ν•΄μ£Όμ–΄ 문제λ₯Ό ν•΄κ²°ν•œλ‹€.
  • array[i][j] = iν–‰ j열에 μ‘΄μž¬ν•˜λŠ” 금의 μ–‘
  • dp[i][j] = iν–‰ jμ—΄κΉŒμ§€μ˜ 졜적의 ν•΄ (얻을 수 μžˆλŠ” 금의 μ΅œλŒ“κ°’)
  • 점화식 : dp[i][j] = array[i][j] + max(dp[i-1][j-1], dp[i][j-1], dp[i+1][j-1])
  • μ΄λ•Œ ν…Œμ΄λΈ”μ— μ ‘κ·Όν•  λ•Œλ§ˆλ‹€ 리슀트의 λ²”μœ„λ₯Ό λ²—μ–΄λ‚˜μ§€ μ•ŠλŠ”μ§€ 체크해야 ν•œλ‹€.
  • νŽΈμ˜μƒ 초기 데이터λ₯Ό λ‹΄λŠ” λ³€μˆ˜ arrayλ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šμ•„λ„ λœλ‹€.
    • λ°”λ‘œ DP ν…Œμ΄λΈ”μ— 초기 데이터λ₯Ό λ‹΄μ•„μ„œ λ‹€μ΄λ‚˜λ―Ή ν”„λ‘œκ·Έλž˜λ°μ„ μ μš©ν•  수 μžˆλ‹€.

<μ½”λ“œ>

# ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€(Test Case) μž…λ ₯
for tc in range(int(input())):
    # κΈˆκ΄‘ 정보 μž…λ ₯
    n, m = map(int, input().split())
    array = list(map(int, input().split()))
    # λ‹€μ΄λ‚˜λ―Ή ν”„λ‘œκ·Έλž˜λ°μ„ μœ„ν•œ 2차원 DP ν…Œμ΄λΈ” μ΄ˆκΈ°ν™”
    dp = []
    index = 0
    for i in range(n):
        dp.append(array[index:index+m])
        index += m
    # λ‹€μ΄λ‚˜λ―Ή ν”„λ‘œκ·Έλž˜λ° 진행
    for j in range(1, m):
        for i in range(n):
            # μ™Όμͺ½ μœ„μ—μ„œ μ˜€λŠ” 경우
            if i==0:left_up = 0
            else: left_up = dp[i-1][j-1]
            # μ™Όμͺ½ μ•„λž˜μ—μ„œ μ˜€λŠ” 경우
            if i==n-1: left_down = 0
            else: left_down = dp[i+1][j-1]
            # μ™Όμͺ½μ—μ„œ μ˜€λŠ” 경우
            left = dp[i][j-1]
            dp[i][j] = dp[i][j] + max(left_up, left_down, left)
    result = 0
    for i in range(n):
        result = max(result, dp[i][m-1])
    print(result)

array[index:index+m] : index λΆ€ν„° (index+m)-1 κΉŒμ§€ 슬라이슀 함

 

 

 

<문제4> 병사 λ°°μΉ˜ν•˜κΈ°

  • Nλͺ…μ˜ 병사가 λ¬΄μž‘μœ„λ‘œ λ‚˜μ—΄λ˜μ–΄ μžˆλ‹€. 각 λ³‘μ‚¬λŠ” νŠΉμ •ν•œ κ°’μ˜ μ „νˆ¬λ ₯을 λ³΄μœ ν•˜κ³  μžˆλ‹€.
  • 병사λ₯Ό λ°°μΉ˜ν•  λ•ŒλŠ” μ „νˆ¬λ ₯이 높은 병사가 μ•žμͺ½μ— μ˜€λ„λ‘ λ‚΄λ¦Όμ°¨μˆœμœΌλ‘œ 배치λ₯Ό ν•˜κ³ μž ν•œλ‹€. λ‹€μ‹œ 말해 μ•žμͺ½μ— μžˆλŠ” λ³‘μ‚¬μ˜ μ „νˆ¬λ ₯이 항상 λ’€μͺ½μ— μžˆλŠ” 병사보닀 λ†’μ•„μ•Ό ν•œλ‹€.
  • λ˜ν•œ 배치 κ³Όμ •μ—μ„œλŠ” νŠΉμ •ν•œ μœ„μΉ˜μ— μžˆλŠ” 병사λ₯Ό μ—΄μ™Έμ‹œν‚€λŠ” 방법을 μ΄μš©ν•œλ‹€. κ·ΈλŸ¬λ©΄μ„œλ„ 남아 μžˆλŠ” λ³‘μ‚¬μ˜ μˆ˜κ°€ μ΅œλŒ€κ°€ λ˜λ„λ‘ ν•˜κ³  μ‹Άλ‹€.

 

<문제 ν•΄κ²°>

  • 이 문제의 κΈ°λ³Έ μ•„μ΄λ””μ–΄λŠ” κ°€μž₯ κΈ΄ μ¦κ°€ν•˜λŠ” λΆ€λΆ„ μˆ˜μ—΄(Longest Increasing Subsequence, LIS)둜 μ•Œλ €μ§„ μ „ν˜•μ μΈ λ‹€μ΄λ‚˜λ―Ή ν”„λ‘œκ·Έλž˜λ° 문제의 아이디어와 κ°™λ‹€.
  • 예λ₯Ό λ“€μ–΄ ν•˜λ‚˜μ˜ μˆ˜μ—΄ array = {4, 2, 5, 8, 4, 11, 15}이 μžˆλ‹€κ³  ν•˜μž.
    • 이 μˆ˜μ—΄μ˜ κ°€μž₯ κΈ΄ μ¦κ°€ν•˜λŠ” λΆ€λΆ„ μˆ˜μ—΄μ€ {4, 5, 8, 11, 15}이닀.
  • λ³Έ λ¬Έμ œλŠ” κ°€μž₯ κΈ΄ κ°μ†Œν•˜λŠ” λΆ€λΆ„ μˆ˜μ—΄μ„ μ°ΎλŠ” 문제둜 μΉ˜ν™˜ν•  수 μžˆμœΌλ―€λ‘œ, LIS μ•Œκ³ λ¦¬μ¦˜μ„ 쑰금 μˆ˜μ •ν•˜μ—¬ μ μš©ν•¨μœΌλ‘œμ¨ 정닡을 λ„μΆœν•  수 μžˆλ‹€.
  • 점화식 : λͺ¨λ“  0 ≤ j < i 에 λŒ€ν•˜μ—¬, D[i] = max(D[i], D[j]+1) if array[j] < array[i] 

 

<μ½”λ“œ>

 

n = int(input())
array = list(map(int, input().split()))
# μˆœμ„œλ₯Ό 뒀집어 '졜μž₯ 증가 λΆ€λΆ„ μˆ˜μ—΄' 문제둜 λ³€ν™˜
array.reverse()

# λ‹€μ΄λ‚˜λ―Ή ν”„λ‘œκ·Έλž˜λ°μ„ μœ„ν•œ 1차원 DP ν…Œμ΄λΈ” μ΄ˆκΈ°ν™”
dp = [1] * n

# κ°€μž₯ κΈ΄ μ¦κ°€ν•˜λŠ” λΆ€λΆ„ μˆ˜μ—΄(LIS) μ•Œκ³ λ¦¬μ¦˜ μˆ˜ν–‰
for i in range(1,n):
    for j in range(0,i):
        if array[j] < array[i]:
            dp[i] = max(dp[i], dp[j]+1)

# μ—΄μ™Έν•΄μ•Ό ν•˜λŠ” λ³‘μ‚¬μ˜ μ΅œμ†Œ 수λ₯Ό 좜λ ₯
print(n-max(dp))
λ°˜μ‘ν˜•