Python

λ°±μ€€ 2011 파이썬

πŸš€NYπŸš€ 2021. 7. 31. 11:38

https://www.acmicpc.net/problem/2011

 

2011번: μ•”ν˜Έμ½”λ“œ

λ‚˜μ˜¬ 수 μžˆλŠ” ν•΄μ„μ˜ κ°€μ§“μˆ˜λ₯Ό κ΅¬ν•˜μ‹œμ˜€. 정닡이 맀우 클 수 μžˆμœΌλ―€λ‘œ, 1000000으둜 λ‚˜λˆˆ λ‚˜λ¨Έμ§€λ₯Ό 좜λ ₯ν•œλ‹€. μ•”ν˜Έκ°€ 잘λͺ»λ˜μ–΄ μ•”ν˜Έλ₯Ό 해석할 수 μ—†λŠ” κ²½μš°μ—λŠ” 0을 좜λ ₯ν•œλ‹€.

www.acmicpc.net

2011λ²ˆμ€ Dynamic Programming λ¬Έμ œμž…λ‹ˆλ‹€.

DP λ¬Έμ œλŠ” ν•œ 번 생각이 μ•ˆ λ‚˜λ©΄ 아무리 식을 길게 μ“°λ©΄μ„œ λ…Έκ°€λ‹€λ₯Ό 해보고 κ·œμΉ™μ„ 찾으렀고 해도 잘λͺ» 찾게 λ˜κ±°λ‚˜, μ•„μ˜ˆ λͺ» 찾게 λ˜λŠ” κ²½μš°κ°€ λ§Žμ€ 것 κ°™μŠ΅λ‹ˆλ‹€(λ¬Όλ‘  μ € ν•œμ •). DP 문제λ₯Ό 더 풀어보고, μ˜ˆμ „μ— ν’€μ—ˆλ˜ 것듀도 λ‹€μ‹œ 풀어보면 데이터가 μŒ“μ΄λ©΄μ„œ μ’€ 더 λΉ λ₯΄κ²Œ κ·œμΉ™μ„ μ°Ύκ³  식을 찾을 수 μžˆμ„ 것 κ°™μ§€λ§Œ, 아직은 그런 κ²½μ§€μ—λŠ” λ‹€λ‹€λ₯΄μ§€ λͺ»ν•œ 것 κ°™μŠ΅λ‹ˆλ‹€.

 

2011λ²ˆμ€ μ•„μ˜ˆ 식을 잘λͺ» μƒκ°ν•΄μ„œ μΆœλ°œν–ˆλ”λ‹ˆ μ˜ˆμ™Έ 처리λ₯Ό λ„ˆλ¬΄ 많이 ν•˜κ²Œ λ˜μ—ˆκ³ , μ½”λ“œκ°€ 잘λͺ»λ˜μ—ˆλ‹€λŠ” κ±Έ κΉ¨λ‹«κ³  ν˜Όμžμ„œ μ”¨λ¦„ν•˜λ‹€κ°€ ꡬ글링 + λ°±μ€€ μ§ˆλ¬Έλ“€μ„ ν†΅ν•΄μ„œ ν•΄κ²°ν–ˆμŠ΅λ‹ˆλ‹€. λ˜ν•œ 질문 κ²Œμ‹œνŒμ„ 보닀 보면 μ•„μ‹œκ² μ§€λ§Œ 2λ…„ 전에 데이터가 λΉ μ§€κ±°λ‚˜ μΆ”κ°€λ˜μ–΄ μ˜ˆμ „ λΈ”λ‘œκ·Έ ν¬μŠ€νŒ…μ„ 보면 '??이게 ν¬ν•¨λœλ‹€κ³ ..?'싢은 μ˜ˆμ œλ“€μ΄λ‚˜ μ˜ˆμ™Έλ“€μ΄ μžˆμŠ΅λ‹ˆλ‹€! 졜근 λΈ”λ‘œκ·Έλ“€μ„ μ°Έκ³ ν•˜λŠ” 게 정닡일 것 κ°™μŠ΅λ‹ˆλ‹€.

 

2011λ²ˆμ€ 점화식을 μ°ΎλŠ” 과정이 쑰금 μƒμ†Œν–ˆμŠ΅λ‹ˆλ‹€. 밑에 손그림과 μ„€λͺ…을 λ‚¨κ²¨λ‘κ² μ§€λ§Œ, 이해가 λ˜μ§€ μ•ŠμœΌμ‹ λ‹€λ©΄ 차라리 κ°€μž₯ ν•˜λ‹¨μ— μ½”λ“œλ₯Ό λ¨Όμ € 보고 λ‹€μ‹œ 손그림과 μ„€λͺ…을 μ½λŠ” 방식이 쑰금 더 μˆ˜μ›”ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

 

μš°μ„  2011번 μ˜ˆμ‹œμ—λŠ” 25114λ₯Ό μž…λ ₯ν–ˆλ”λ‹ˆ 6이 λ‚˜μ˜΅λ‹ˆλ‹€.

 

일단 25114μ—μ„œ 6이 λ‚˜μ˜€κ²Œ 된 과정을 μ§κ΄€μ μœΌλ‘œ μ‚΄νŽ΄λ³΄λ©΄, 

2/5/1/1/4

25/1/1/4

25/11/4

25/1/14

2/5/11/4

2/5/1/14

 

μ΄λ ‡κ²Œ μ—¬μ„―κ°€μ§€κ°€ μžˆμŠ΅λ‹ˆλ‹€. 

 

μ—¬μ„―κ°€μ§€λ₯Ό 직접 찾아보면 μš°μ„  i번째 μΈλ±μŠ€μΌλ•Œ i-1번째 인덱슀λ₯Ό 10의 자리수둜 λ‘˜ 수 μžˆλŠλƒ μ—†λŠλƒμ— 따라 경우의 μˆ˜κ°€ λ°œμƒν•œλ‹€λŠ” κ²ƒκΉŒμ§€λŠ” 이해할 수 μžˆμ„ 것 κ°™μŠ΅λ‹ˆλ‹€.

더 ν’€μ–΄μ„œ μ„€λͺ…ν•˜μžλ©΄, μ•žμ„œ 2번째 인덱슀(5)μ—μ„œ 1번째 인덱슀(2)λ₯Ό μ°Έκ³ ν–ˆμ„λ•Œ, 10의 자리수λ₯Ό 2라고 두고 5와 ν•©μΉ˜λ©΄ 25κ°€ λ©λ‹ˆλ‹€. 25λŠ” μ•”ν˜Έν™” ν•˜λŠ” λ²”μœ„(1~26)에 ν¬ν•¨λ˜κΈ° λ•Œλ¬Έμ— 2,5둜 ν‘œν˜„ν•  μˆ˜λ„ μžˆμ§€λ§Œ, ν†΅μœΌλ‘œ 25둜 ν‘œν˜„ν•  μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€. 즉 경우의 μˆ˜κ°€ 2개 μƒκΈ°κ²Œ λ©λ‹ˆλ‹€.

κ·ΈλŸ¬λ‚˜ 3번재 인덱슀인 1μ—μ„œλŠ” μ–˜κΈ°κ°€ λ‹¬λΌμ§‘λ‹ˆλ‹€. 2번째 인덱슀 값이 5μ΄λ―€λ‘œ λ§Œμ•½ 5κ°€ μ‹­μ˜ 자리수둜 였게 λœλ‹€λ©΄ 51이 λ˜λŠ”λ°, μ΄λŠ” λ²”μœ„μ— μ–΄κΈ‹λ‚˜λŠ” κ°’μž…λ‹ˆλ‹€. 즉, 였직 5/1둜 ν‘œν˜„ν•˜κ²Œ λ˜μ–΄ 경우의 μˆ˜κ°€ 1κ°œκ°€ λ©λ‹ˆλ‹€.

 

μ—¬κΈ°μ„œ dp 식을 μ–΄λ–»κ²Œ μ„Έμ›Œμ•Ό ν•  μ§€ 고민을 μ˜¬λ°”λ₯΄κ²Œ ν•˜λŠλƒ λΉ™λΉ™ λ‘˜λŸ¬ κ°€λŠλƒμ˜ 차이가 생길 것 κ°™μŠ΅λ‹ˆλ‹€.

 

μ΄μ œλŠ” μ†μœΌλ‘œ κ·Έλ¦° 그림을 μ²¨λΆ€ν•˜λ©° μ„€λͺ…을 μ§„ν–‰ν•˜κ² μŠ΅λ‹ˆλ‹€.

κ²°κ³ΌλΆ€ν„°

ν•„κΈ°ν•˜λŠ” λͺ¨μŠ΅μ„ GIF둜 λ§Œλ“€μ—ˆλŠ”λ°, 쀑간쀑간에 ν™”λ©΄ λ…Ήν™”κ°€ μ’€ μž˜λ €μ„œ κ²°κ³Ό 이미지뢀터 μ²¨λΆ€ν•©λ‹ˆλ‹€. 이걸둜 λ°”λ‘œ 이해가 λœλ‹€κ³  ν•˜μ‹œλ©΄ κ·Έλƒ₯ λ°”λ‘œ 문제λ₯Ό ν’€λ©΄ 될 것 κ°™μŠ΅λ‹ˆλ‹€.

 

κ°€μž₯ 처음 dp의 μ΄ˆκΈ°κ°’λ“€μ€ 0이라고 ν•©μ‹œλ‹€. dp = [0,0,0,0,0] μ΄λ•Œ 인덱슀 μ‹œμž‘μ΄ 0이 μ•„λ‹ˆλΌ 1이라고 κ°€μ •ν•©λ‹ˆλ‹€. 

 

1. 2μΌλ•Œ 2만 λ˜λ―€λ‘œ 경우의 μˆ˜λŠ” 1개 μž…λ‹ˆλ‹€. dp[1] = 1

 

2. 5μΌλ•ŒλŠ” μž…λ ₯ 값이 25라고 μƒκ°ν•œλ‹€λ©΄ 2,5 그리고 25 μ΄λ ‡κ²Œ 경우의 μˆ˜κ°€ 각각 1개 μ”© λ‚˜μ˜΅λ‹ˆλ‹€. μ΅œμ’… 합은 dp[2] = 2

 

3. μ—¬κΈ°μ„œλΆ€ν„° 쑰금 ν—·κ°ˆλ¦΄ 수 μžˆλŠ”λ°, κ·ΈλŸ΄λ•ŒλŠ” 직접 μ†μœΌλ‘œ μ¨μ„œ 경우의 수λ₯Ό 세보면 λ©λ‹ˆλ‹€. 251μΌλ•ŒλŠ” 51이 λ‚˜μ˜¬ 수 μ—†κΈ° λ•Œλ¬Έμ— μ•žμ„œ dp[2] = 2μ—μ„œ λ³€ν™”κ°€ λ°œμƒν•  수 μ—†μŠ΅λ‹ˆλ‹€. 2/5/1 그리고 25/1 μ΄λ ‡κ²Œ 두 κ°€μ§€ κ²½μš°λ°–μ— μ—†κΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€.

즉, dp[3] += dp[2] -> dp[3] = 2

 

4. 그리고 2511μΌλ•ŒλŠ” 1/1, 11이 λ‚˜μ˜¬ 수 있기 λ•Œλ¬Έμ— 경우의 μˆ˜κ°€ 2κ°€μ§€ λ°œμƒν•©λ‹ˆλ‹€. 

 

펜 κ΅΅κΈ°λ‚˜ 색을 λ°”κΎΈκΈ° μœ„ν•΄ 창을 μ—΄μ—ˆλ‹€ λ‹«μ•˜λ‹€ ν•΄μ„œ gif 가독성이 쑰금 λ–¨μ–΄μ§€λ„€μš” γ… γ…  

μ—¬κΈ°μ„œ 이제 1μΌλ•Œ 11μΌλ•Œ, 각각 dp[3]의 κ°’κ³Ό dp[2]의 값을 ν™”μ‚΄ν‘œλ‘œ λŒμ–΄λ‹€κ°€ ν•©ν–ˆλŠ”λ°, κ·Έ μ΄μœ μ— λŒ€ν•΄ 더 μžμ„Ένžˆ μ„€λͺ…ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

 

1μΌλ•ŒλŠ” μ•žμ— 2,5,1을 μ΄μš©ν•œ 수의 쑰합에 1을 덧뢙인 것과 κ°™μŠ΅λ‹ˆλ‹€. dp[3] = 2μ˜€λŠ”λ°, 2/5/1/1 μ΄κ±°λ‚˜ 25/1/1 일 수 밖에 없기에 dp 값이 λ³€ν•  μˆ˜κ°€ μ—†λ‹€λŠ” λœ»μž…λ‹ˆλ‹€. 즉, dp[4] += dp[3]일 수 밖에 μ—†μŠ΅λ‹ˆλ‹€. 

 

11μΌλ•ŒλŠ” λ™μΌν•œ μ›λ¦¬μ΄μ§€λ§Œ, 2,5,1,1μ—μ„œ 1,1λ₯Ό λΊ€, 즉 dp[2]의 값을 λ”ν•΄μ€˜μ•Ό ν•©λ‹ˆλ‹€. 2/5/11μ΄κ±°λ‚˜ 25/11이기 λ•Œλ¬Έμž…λ‹ˆλ‹€. 즉, dp[4] += dp[2]κ°€ λ©λ‹ˆλ‹€. 

 

5. λ§ˆμ§€λ§‰, 4μΌλ•Œ, 25114μΌλ•Œλ„ λ§ˆμ°¬κ°€μ§€ μž…λ‹ˆλ‹€. dp[5] += dp[3], dp[5] += dp[4] 

 

κ·Έλ ‡λ‹€λ©΄ 이제 점화식을 λ§Œλ“€κ³  결둠을 λ§Œλ“€ 수 μžˆμ„ 것 κ°™μŠ΅λ‹ˆλ‹€.

즉, 1의 자리 수만 κ°€λŠ₯ν•˜λ‹€λ©΄ dp[i] += dp[i-1],

κ·Έ μ „ 인덱슀 값을 10의 자리수둜 ν¬ν•¨ν•΄μ„œ 그게 λ²”μœ„(10~26)에 λ“€μ–΄κ°„λ‹€λ©΄ dp[i] += dp[i-2]

 

그런데 또 μ•žμ„œ λ³Έ 두 번째 인덱슀, 5μΌλ•Œκ°€ 그럼 논리에 λ§žμ§€ μ•ŠμŠ΅λ‹ˆλ‹€. dp[2] += dp[1], dp[2] += dp[0]이 λ˜μ–΄μ•Ό ν•˜λŠ”λ° μš°λ¦¬λŠ” ν˜„μž¬ dp[0]이 μ—†μ£ . 즉, dp의 길이가 μž…λ ₯κ°’μ˜ 길이보닀 1개 더 κΈΈμ–΄μ•Ό ν•˜κ³ , dp[0]의 κ°’μ—λŠ” 1이 λ””ν΄νŠΈλ‘œ λ“€μ–΄κ°€μžˆμ–΄μ•Ό 논리가 λ§žμŠ΅λ‹ˆλ‹€. 

 

그리고 μ €λŠ” 거기에 λ§žμΆ°μ„œ μž…λ ₯κ°’ code에도 μ²˜μŒμ— code[0] = 0μ΄λΌλŠ” μ˜λ―Έμ—†λŠ” 값을 λ„£μ–΄μ£Όμ–΄ 인덱슀λ₯Ό λ§žμΆ°μ£Όμ—ˆμŠ΅λ‹ˆλ‹€.

 

κ·Έλ ‡λ‹€λ©΄ dp 계산 둜직 식을 μ΄λ ‡κ²Œ μ •μ˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

length = len(code) #μž…λ ₯받은 μ•”ν˜Έ μˆ«μžκ°€ code
dp = [0] * length
dp[0] = dp[1] = 1 #dp[0] = 1 λ””ν΄νŠΈκ°’, dp[1]은 μ˜ˆμ‹œ 25114μ—μ„œ 2μ—λ§Œ ν•΄λ‹Ήν•˜λ―€λ‘œ 무쑰건 1이 됨.

for i in range(2, length):
    first = int(code[i]) #ν•œμžλ¦¬ 수
    tenth = int(code[i-1])*10 + int(code[i])#κ·Έ μ „ 인덱슀λ₯Ό μ°Έκ³ ν•΄μ„œ 두 자리수둜 ꡬ성
    if first > 0: dp[i] += dp[i-1] #ν•œ 자리 μˆ˜κ°€ 0보닀 ν΄λ•Œ
    if tenth >= 10 and tenth <= 26: dp[i] += dp[i-2] #두 자리 μˆ˜κ°€ 10이상 26μ΄ν•˜μΌλ•Œ

 

이제 λ‘œμ§μ„ λ‹€ 짰으면 μ˜ˆμ™Έ 처리λ₯Ό ν•΄μ€˜μ•Ό ν•©λ‹ˆλ‹€.

λ¬Έμ œμ—μ„œλŠ” 'μ•”ν˜Έκ°€ 잘λͺ»λ˜μ–΄ μ•”ν˜Έλ₯Ό 해석할 수 μ—†λŠ” κ²½μš°μ—λŠ” 0을 좜λ ₯ν•œλ‹€.' 라고 λ˜μ–΄μžˆμŠ΅λ‹ˆλ‹€. μ•”ν˜Έκ°€ 잘λͺ»λœλ‹€λŠ” 건 μ–΄λ–€ κ²½μš°μΈμ§€ λ‹¨μˆœνžˆ 생각해보면, μš°μ„  0이 μžˆκ² μŠ΅λ‹ˆλ‹€. λ²”μœ„λŠ” 1~26인데 0을 μž…λ ₯ λ°›μœΌλ©΄ 해석할 수 μ—†κ² μ£ . 00020, 001, 01 λ“± 맨 μ²˜μŒμ— 0이 λΆ™μœΌλ©΄ λ‹€ 잘λͺ»λœ 경우라고 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€. 

 

κ·Έλ ‡λ‹€λ©΄ 0이 쀑간에 λ“€μ–΄κ°€ 있으면 μ–΄λ–»κ²Œ λ κΉŒμš”? μ˜ˆμ‹œλ‘œ 1304λ₯Ό μƒκ°ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€. 쀑간에 10, 20이 μ•„λ‹Œ 30이 λ“€μ–΄κ°€ μžˆλ‹€κ³  생각할 μˆ˜λ„ 있고, λΆˆν•„μš”ν•œ 0이 λ“€μ–΄κ°„ 04κ°€ λ“€μ–΄κ°”λ‹€κ³  생각해도 μ’‹κ² μŠ΅λ‹ˆλ‹€. 이것 λ˜ν•œ μ •ν•΄μ§„ κ·œμΉ™μ— μ–΄κΈ‹λ‚˜λŠ” μ•”ν˜Έμ΄κΈ° λ•Œλ¬Έμ— 0이 좜λ ₯λ˜μ–΄μ•Ό ν•©λ‹ˆλ‹€. κ·Έλ ‡λ‹€λ©΄ 맨 μ²˜μŒμ— μž…λ ₯ λ°›μžλ§ˆμž for문을 λŒλ©΄μ„œ 이런 μ˜ˆμ™Έλ₯Ό λ”°λ‘œ κ±ΈλŸ¬λ‚΄μ•Ό ν• κΉŒμš”? κ·ΈλŸ΄ν•„μš”κ°€ μ—†μŠ΅λ‹ˆλ‹€.

 

저희가 μ§  λ‘œμ§μ— λ”°λ₯΄λ©΄, μš°μ„  dp[1] = 1, dp[2] = 2κΉŒμ§€λŠ” μ‰½κ²Œ 생각할 수 μžˆμŠ΅λ‹ˆλ‹€.

κ·Έ λ‹€μŒ indexκ°€ 3이 λ˜μ—ˆκ³ , code[3] = 0μž…λ‹ˆλ‹€. ν•œμžλ¦¬μˆ˜ μΌλ•ŒλŠ” 0이고, 두 자리 수 μΌλ•ŒλŠ” 30이죠. μ €ν¬μ˜ μ‘°κ±΄λ¬Έμ—λŠ” 아무것도 걸리지 μ•ŠκΈ° λ•Œλ¬Έμ— dp[3]μ—λŠ” κ·Έ μ–΄λ– ν•œ 값도 더해지지 μ•ŠμŠ΅λ‹ˆλ‹€.

즉, dp[3] = 0μ΄λΌλŠ” 값이 κ·ΈλŒ€λ‘œ μœ μ§€λ©λ‹ˆλ‹€. 그리고 indexκ°€ 4κ°€ 되면 dp[3] = 0이기 λ•Œλ¬Έμ— dp[4] = 0이 λ˜μ–΄ κ²°κ³Ό 값은 μ•”ν˜Έλ₯Ό 해석할 수 μ—†λŠ” 경우인 0이 좜λ ₯λ©λ‹ˆλ‹€. 

 

즉, 맨 μ²˜μŒμ— 0이 λΆ™λŠ” 경우만 μž…λ ₯ 받은 ν›„ λ°”λ‘œ 처리 ν•΄μ£Όλ©΄, λ‹€λ₯Έ μ˜ˆμ™Έλ“€μ€ 둜직 λ‚΄μ—μ„œ μ•Œμ•„μ„œ 0을 좜λ ₯ν•  수 있게 λ©λ‹ˆλ‹€.

 

 

<μ „μ²΄μ½”λ“œ>

import sys
code = [0] #인덱슀 κ°’ λ§žμΆ”κΈ°μš© 0 μ΄ˆκΈ°κ°’
code += list(sys.stdin.readline())
code.pop() #'\n'날리기

if code[1] == '0': 
    print(0)
    exit(0)

length = len(code)
dp = [0] * length
dp[0] = dp[1] = 1

for i in range(2, length):
    first = int(code[i])
    tenth = int(code[i-1])*10 + int(code[i])
    if first > 0: dp[i] += dp[i-1]
    if tenth >= 10 and tenth <= 26: dp[i] += dp[i-2]

print(dp[length-1]%1000000)

 

λΈ”λ‘œκ·Έλ₯Ό μ—¬λŸ¬ ꡰ데 λ΄€λŠ”λ° μ΄ν•΄ν•˜κΈ°κ°€ 쉽지 μ•Šμ•„μ„œ μ΅œμ„ μ„ λ‹€ν•΄μ„œ 풀이λ₯Ό 해보렀고 ν–ˆμŠ΅λ‹ˆλ‹€. 이해가 λ˜μ—ˆμ„μ§€λŠ” λͺ¨λ₯΄κ² μŠ΅λ‹ˆλ‹€γ… .γ…  ν˜Ήμ‹œ μ œκ°€ 잘λͺ» κΈ°μž¬ν•œ 뢀뢄이 μžˆλ‹€λ©΄ μ–Έμ œλ“ μ§€ λŒ“κΈ€ λ‚¨κ²¨μ£Όμ„Έμš” κ°μ‚¬ν•©λ‹ˆλ‹€