Problem Solving/[ν•­ν•΄99] TIL

99클럽 μ½”ν…Œ μŠ€ν„°λ”” 16일차 TIL / 그리디

geum 2024. 11. 12. 21:13

✏️ 문제

[λ°±μ€€/BOJ] 2847번: κ²Œμž„μ„ λ§Œλ“  동쀀이(https://www.acmicpc.net/problem/2847)

 

ν•™κ΅μ—μ„œ κ·Έλž˜ν”½μŠ€ μˆ˜μ—…μ„ 듀은 λ™μ€€μ΄λŠ” μˆ˜μ—…μ‹œκ°„μ— 듀은 λ‚΄μš©μ„ λ°”νƒ•μœΌλ‘œ 슀마트폰 κ²Œμž„μ„ λ§Œλ“€μ—ˆλ‹€. κ²Œμž„μ—λŠ” 총 N개의 레벨이 있고, 각 λ ˆλ²¨μ„ 클리어할 λ•Œ λ§ˆλ‹€ μ μˆ˜κ°€ 주어진닀. ν”Œλ ˆμ΄μ–΄μ˜ μ μˆ˜λŠ” λ ˆλ²¨μ„ ν΄λ¦¬μ–΄ν•˜λ©΄μ„œ 얻은 점수의 ν•©μœΌλ‘œ, 이 점수λ₯Ό λ°”νƒ•μœΌλ‘œ 온라인 μˆœμœ„λ₯Ό 맀긴닀. λ™μ€€μ΄λŠ” λ ˆλ²¨μ„ λ‚œμ΄λ„ 순으둜 λ°°μΉ˜ν–ˆλ‹€. ν•˜μ§€λ§Œ, μ‹€μˆ˜λ‘œ μ‰¬μš΄ 레벨이 μ–΄λ €μš΄ λ ˆλ²¨λ³΄λ‹€ 점수λ₯Ό 많이 λ°›λŠ” 경우λ₯Ό λ§Œλ“€μ—ˆλ‹€.

이 문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ λ™μ€€μ΄λŠ” νŠΉμ • 레벨의 점수λ₯Ό κ°μ†Œμ‹œν‚€λ €κ³  ν•œλ‹€. μ΄λ ‡κ²Œν•΄μ„œ 각 λ ˆλ²¨μ„ 클리어할 λ•Œ μ£ΌλŠ” μ μˆ˜κ°€ μ¦κ°€ν•˜κ²Œ λ§Œλ“€λ €κ³  ν•œλ‹€.

각 λ ˆλ²¨μ„ 클리어할 λ•Œ μ–»λŠ” μ μˆ˜κ°€ μ£Όμ–΄μ‘Œμ„ λ•Œ, λͺ‡ 번 κ°μ†Œμ‹œν‚€λ©΄ λ˜λŠ”μ§€ κ΅¬ν•˜λŠ” ν”„λ‘œκ·Έλž¨μ„ μž‘μ„±ν•˜μ‹œμ˜€. μ μˆ˜λŠ” 항상 μ–‘μˆ˜μ΄μ–΄μ•Ό ν•˜κ³ , 1만큼 κ°μ†Œμ‹œν‚€λŠ” 것이 1λ²ˆμ΄λ‹€. 항상 닡이 μ‘΄μž¬ν•˜λŠ” 경우만 주어진닀. 정닡이 μ—¬λŸ¬ 가지인 κ²½μš°μ—λŠ” μ μˆ˜λ₯Ό λ‚΄λ¦¬λŠ” 것을 μ΅œμ†Œν•œμœΌλ‘œ ν•˜λŠ” 방법을 μ°Ύμ•„μ•Ό ν•œλ‹€.

 

πŸ€– μž…μΆœλ ₯ μ˜ˆμ‹œ

 

🧐 λ‚œμ΄λ„/μ†Œμš” μ‹œκ°„

βœ… λ‚œμ΄λ„: solved.ac κΈ°μ€€ S4

βœ… μ†Œμš” μ‹œκ°„: μ˜ˆμ „μ— ν’€μ—ˆλ˜ 문제라 μΈ‘μ • X

βœ… ꢌμž₯ μ‹œκ°„: 1μ‹œκ°„ 15λΆ„

 

πŸ’‘ 풀이

1) 문제 μœ ν˜• νŒŒμ•…

μ˜ˆμ „μ— ν’€ λ•ŒλŠ” μ•Œκ³ λ¦¬μ¦˜μ„ 크게 μ‹ κ²½ 쓰지 μ•Šμ•˜μ—ˆλŠ”λ° 였늘 μ•Œκ³ λ¦¬μ¦˜ λΆ„λ₯˜λ₯Ό λ³΄λ‹ˆκΉŒ 그리디 μ•Œκ³ λ¦¬μ¦˜μ΄μ—ˆλ‹€.

 

λ‚΄κ°€ μ•Œκ³  μžˆλŠ” κ·Έλ¦¬λ””μ˜ νŠΉμ§•μ€ '맀 μˆœκ°„ κ·Έ μƒν™©μ—μ„œ 졜적인 선택을 ν•˜μ§€λ§Œ 전체 κ²°κ³Όμ—μ„œλ„ κ·Έ κ²°κ³Όκ°€ μ΅œμ μž„μ„ 보μž₯ν•  μˆ˜λŠ” μ—†μŒ'인데 이 κ΄€μ μ—μ„œ μƒκ°ν–ˆμ„ λ•Œ 이 λ¬Έμ œκ°€ μ™œ 그리디 μ•Œκ³ λ¦¬μ¦˜μ— μ†ν•˜μ§€? μ‹Άμ—ˆλ‹€. 챗지피티가 항상 μ •λ‹΅λ§Œ μ–˜κΈ°ν•˜λŠ” 건 μ•„λ‹ˆμ§€λ§Œ 이해에 도움이 되고자 살짝쿡 μ§ˆλ¬Έν•œ ν›„ λ‚΄κ°€ μ΄ν•΄ν•œλŒ€λ‘œ 정리해봀닀.


 

문제의 μš”κ΅¬μ‚¬ν•­

- ν•„μš”ν•œ 경우 λ ˆλ²¨λ§ˆλ‹€ 점수λ₯Ό 내리고, 이 λ•Œ μ΅œμ†Œ 횟수둜 λͺ¨λ“  레벨의 μ μˆ˜κ°€ μ˜€λ¦„μ°¨μˆœμ„ μœ μ§€ν•˜λ„λ‘ ν•΄μ•Ό 함

 

μ™œ 그리디인지?

- ν˜„μž¬ 확인 쀑인 λ ˆλ²¨μ—μ„œ ν•  수 μžˆλŠ” 졜적의 선택은 ν˜„μž¬ 레벨의 μ μˆ˜κ°€ λ‹€μŒ 레벨의 μ μˆ˜λ³΄λ‹€ 1점 μž‘κ²Œ ν•˜λŠ” 것

- κ°€μž₯ μ–΄λ €μš΄ λ ˆλ²¨μ—μ„œ μ‰¬μš΄ 레벨 λ°©ν–₯으둜 점수λ₯Ό ν™•μΈν•˜λ©΄μ„œ ν•„μš”ν•  λ•Œ 점수λ₯Ό κΉŽλŠ” 것이 κ°€μž₯ 졜적의 선택이라고 ν•  수 있음 - 높은 μ μˆ˜λΆ€ν„° ν™•μΈν•˜λ©° 점수λ₯Ό μ‘°μ •ν•  경우 이전 λ ˆλ²¨μ— λŒ€ν•΄ λ‹€μ‹œ 확인할 ν•„μš”κ°€ μ—†μœΌλ―€λ‘œ ν˜„μž¬ 선택이 이후 선택에 영ν–₯을 주지 μ•ŠμŒ(독립적)

 

2) 아이디어 흐름

β‘  μ˜€λ¦„μ°¨μˆœ 정닡지λ₯Ό λ§Œλ“€μ–΄λ‘κ³  주어진 λ¦¬μŠ€νŠΈμ™€ 차이 계산

제일 λ¨Όμ € λ– μ˜¬λ¦° 방법은 주어진 숫자 리슀트의 맨 λ’€ μ›μ†Œκ°€ κ°€μž₯ 크닀고 정해두고 ν•΄λ‹Ή κ°’μœΌλ‘œ λλ‚˜λŠ” μ˜€λ¦„μ°¨μˆœ 리슀트λ₯Ό μ •λ‹΅ 리슀트둜 μ‚¬μš©ν•˜λŠ” 것이닀. 이게 무슨 말인가 ν•˜λ©΄..!

 

μž…λ ₯ 리슀트 [5, 3, 7, 5] (μž…μΆœλ ₯ μ˜ˆμ‹œ 2)

μ •λ‹΅ 리슀트 [2, 3, 4, 5] (λ‚΄κ°€ μƒκ°ν•œ λ°©μ‹μ—μ„œ μ •λ‹΅μ§€λ‘œ μ“°κ³ μž ν•˜λŠ” 리슀트)

 

κ½€λ‚˜ 그럴 λ“―ν•œ λ°©λ²•μ²˜λŸΌ 보이기도 ν•˜κ³  두 개의 μ˜ˆμ‹œμ—μ„œ λ‚˜μ™€μ•Ό ν•˜λŠ” 값을 잘 좜λ ₯ν•˜κΈ°λ„ ν•΄μ„œ μ œμΆœν–ˆλ”λ‹ˆ κ²°κ³ΌλŠ” μ˜€λ‹΅μ΄μ—ˆλ‹€.

 

λ°˜λ‘€λ₯Ό κ³ λ―Όν•˜λ‹€κ°€ 이 λ°©μ‹μœΌλ‘œ μ²˜λ¦¬ν•˜μ§€ λͺ»ν•˜λŠ” 경우λ₯Ό ν•˜λ‚˜ λ°œκ²¬ν–ˆλ‹€.

 

μž…λ ₯ 리슀트 [3, 3, 7, 6]

μ •λ‹΅ 좜λ ₯ 5 → [2, 3, 4, 5]κ°€ λ§Œλ“€μ–΄μ§€λ„λ‘ ν•΄μ•Ό 함

μ‹€μ œ 좜λ ₯ 3 → [3, 4, 5, 6]을 μ •λ‹΅ 리슀트둜 μ‚¬μš©

 

μœ„μ˜ 2번 인덱슀처럼 μ •λ‹΅ λ¦¬μŠ€νŠΈμ—μ„œ 각 μΈλ±μŠ€μ— μžˆμ–΄μ•Ό ν•˜λŠ” 값보닀 μž‘μ€ 값이 ν• λ‹Ήλœ 경우, 값을 올릴 μˆ˜λŠ” μ—†κΈ° λ•Œλ¬Έμ— μ œλŒ€λ‘œ 된 닡을 ꡬ할 수 μ—†λ‹€.

 

# 첫 번째 μ˜€λ‹΅ μ½”λ“œ

import sys

input = sys.stdin.readline

N = int(input())

score = []

for _ in range(N):
    score.append(int(input()))

ascending = [score[-1]-i for i in range(N)]

answer = 0

for real, want in zip(score, sorted(ascending)):
    answer += abs(real-want)

print(answer)

 

 

β‘‘ λ‚΄λ¦Όμ°¨μˆœμœΌλ‘œ μ ‘κ·Όν•˜κΈ°

μ˜€λ¦„μ°¨μˆœ λ°©μ‹μœΌλ‘œ 풀어보렀고 계속 κ³ λ―Όν–ˆλŠ”λ° 이전 값을 μ‹ κ²½ 써야 ν•˜λŠ” 상황이 자꾸 μƒκΈ°κΈΈλž˜ λ„ˆλ¬΄ λ³΅μž‘ν•΄μ„œ 일단 λ’€μ§‘μ–΄μ„œ μƒκ°ν•΄λ³΄κΈ°λ‘œ ν–ˆλ‹€. μž…μΆœλ ₯ μ˜ˆμ‹œ 2μ—μ„œ [5, 7, 3, 5] 리슀트둜 μ ‘κ·Όν•˜λŠ” 방식이닀.

 

핡심 λ‘œμ§μ€ 1) i+1번째 μš”μ†Œκ°€ i번째 μš”μ†Œλ³΄λ‹€ ν¬κ±°λ‚˜ κ°™μœΌλ©΄ 2) i+1번째 μš”μ†Œμ˜ 값을 (i번째 μš”μ†Œμ˜ κ°’)-1둜 κ°±μ‹ ν•˜κ³ , 3) 횟수λ₯Ό κΈ°λ‘ν•˜κΈ° μœ„ν•œ λ³€μˆ˜μ— (i번째 μš”μ†Œμ˜ κ°’)-1λ§ŒνΌμ„ λ”ν•΄μ£ΌλŠ” 것이닀.

 

그런데 μž…μΆœλ ₯ 예제 2λŠ” 잘 좜λ ₯λμ§€λ§Œ μž…μΆœλ ₯ 예제 1이 μ΄μƒν•˜κ²Œ 좜λ ₯λΌμ„œ λ‹€μ‹œ ν™•μΈν•΄λ³΄λ‹ˆ answer 값에 score[i]-1 값을 λ°”λ‘œ λ”ν•˜λŠ” 게 λ¬Έμ œμ˜€λ‹€. [5, 7, 3, 5] 리슀트둜 확인해보면 7이 5보닀 크기 λ•Œλ¬Έμ— κ°μ†Œκ°€ 이뀄지고 7μ—μ„œ 4둜 κ°μ†Œν•œ 만큼 3이 더해져야 ν•˜λŠ”λ° 4λ₯Ό 더해주고 μžˆλŠ” 것이닀.

 

μž…μΆœλ ₯ 예제 2κ°€ ν†΅κ³Όν•œ 게 μ–΄μ©Œλ©΄ μ–»μ–΄ κ±Έλ¦° ν†΅κ³ΌμΌμˆ˜λ„ μžˆκ² λ‹€ μ‹Άμ–΄ μ € 뢀뢄을 μˆ˜μ •ν•˜κΈ°λ‘œ ν–ˆλ‹€.

 

# 두 번째 μ˜€λ‹΅ μ½”λ“œ

import sys

input = sys.stdin.readline

N = int(input())

score = []

for _ in range(N):
    score.append(int(input()))

answer = 0

score = score[::-1]

for i in range(0, len(score)-1):
    if score[i+1] >= score[i]:
        new_value = score[i]-1
        
        answer += new_value
        
        score[i+1] = new_value

print(answer)

 

3) κ΅¬ν˜„

import sys

input = sys.stdin.readline

N = int(input())

score = []

for _ in range(N):
    score.append(int(input()))

answer = 0

score = score[::-1]

for i in range(0, len(score)-1):
    if score[i+1] >= score[i]:
        new_value = score[i]-1
        
        answer += score[i+1]-score[i]+1
        
        score[i+1] = new_value

print(answer)

 

λ‹€λ₯Έ 뢀뢄은 두 번째 μ˜€λ‹΅ μ½”λ“œμ™€ λͺ¨λ‘ λ™μΌν•˜κ³  answer λ³€μˆ˜μ— λ”ν•΄μ§€λŠ” κ°’λ§Œ μˆ˜μ •ν•΄μ£Όλ‹ˆκΉŒ 톡과할 수 μžˆμ—ˆλ‹€.

 

4) μ–΄λ €μ› λ˜ 점/배운 μ 

🚨 μ–΄λ €μ› λ˜ 점

  • λ’€μ—μ„œλΆ€ν„° μ²˜λ¦¬ν•˜λŠ” κ±Έ ν•œ λ²ˆμ— λ– μ˜¬λ¦¬λŠ” 것
  • μ˜€λ¦„μ°¨μˆœμœΌλ‘œ μ²˜λ¦¬ν•˜λ €κ³  ν•˜λ‹ˆκΉŒ ν˜„μž¬ 값보닀 더 μž‘μ€ 값이 이전에 λ‚˜μ™”λŠ”μ§€λ₯Ό μ‹ κ²½ 써야 ν–ˆκ³  μ½”λ“œκ°€ λ„ˆλ¬΄ λ³΅μž‘ν•΄μ§ˆ 것 κ°™μ•˜λ‹€. μ—­μˆœμœΌλ‘œ ν™•μΈν•˜λŠ” 이유λ₯Ό μ•Œκ³  μ“΄ 게 μ•„λ‹ˆλΌ μ†Œ λ’·κ±ΈμŒμ§ˆ ν•˜λ‹€ μ₯ μž‘μ€ 격의 풀이 λŠλ‚Œ?

❗ 배운 점

  • ν˜„μž¬μ˜ 선택이 이후 선택 과정에 영ν–₯을 λ―ΈμΉ˜μ§€ μ•ŠλŠ” 것도 그리디 μ•Œκ³ λ¦¬μ¦˜μ„ μ μš©ν•  수 μžˆλŠ” 쑰건에 ν•΄λ‹Ήν•œλ‹€. 근데 이건 문제λ₯Ό 보고 ν•œ λ²ˆμ— νŒŒμ•…ν•˜κΈ°λŠ” 쑰금 쉽지 μ•Šμ„μˆ˜λ„ μžˆμ„ 것 κ°™λ‹€λŠ” 생각이 λ“ λ‹€.