-
[CodeKata] 프로그래머스(Lv3) : 스티커 모으기(2)Algorithm 2022. 2. 5. 18:43반응형
🥋 Oooth More!! (Level 3)
🧮 풀이
먼저, sticker 배열의 홀수 요소들만의 합(oSum), 짝수 요소들만의 합(eSum) 중 최대값을 max에 저장했다.
그리고, 2가지 출발점(s1 = 최대값, s2 = 최대값의 인접값 중 큰 값) 부터 1칸을 건너뛰고 두 번째, 세 번째 중 최대값을 누적하면서 스티커를 뜯은 값과 최대값(max)를 비교해 큰 값을 저장했다.
이처럼 풀이했을 때, 예외 케이스가 발생할 수 있는 방법이었기에 정확성도 50% 미만이고, 반복적인 순회로 효율성을 통과하지 못했다.
function solution(sticker) { let oSum = 0, eSum = 0; for (let i in sticker) { if (+i % 2 === 0) { eSum += sticker[i] } else { oSum += sticker[i] } } let max = Math.max(oSum, eSum); const e = sticker.length-1 const s1 = sticker.indexOf(Math.max(...sticker)) const s2 = sticker.indexOf(Math.max((sticker[s1-1] ?? sticker[e]), (sticker[s1+1] ?? sticker[0]))) for (let s of [s1,s2]) { let arr = [...sticker]; arr = [...arr.splice(s-1 < 0 ? e : s-1), ...arr.splice(0,(s-1 < 0 ? e : s-1))] let i = 1; let nowMax = 0; while (i <= e) { nowMax += arr[i]; i = arr[i+2] < arr[i+3] ? i+3 : i+2 } max = Math.max(max, nowMax) } return max }
🖇 리뷰
모범답안을 보니 생각보다 풀이는 간단했다.
하지만 DP(Dynamic Programming)을 통해 특정 인덱스에서 가질 수 있는 최대 누적값을 저장해나가는 방법,
그리고 이를 첫 번째 스티커를 뜯는 경우 / 안뜯는 경우 2가지를 고려해서 순회한다는 아이디어를 발상하는 초기과정이 매우 어려웠을 것이다.
* DP(Dynamic Programming) 참고링크 : https://velog.io/@chelsea/1-%EB%8F%99%EC%A0%81-%EA%B3%84%ED%9A%8D%EB%B2%95Dynamic-Programming-DP
function solution(sticker) { const len = sticker.length + 2; const dp1 = new Array(len).fill(0); const dp2 = new Array(len).fill(0); if(sticker.length === 1) return sticker[0]; for(let i = 2; i < len-1; i++) dp1[i] = Math.max(dp1[i-1], dp1[i-2] + sticker[i-2]); for(let i = 3; i < len; i++) dp2[i] = Math.max(dp2[i-1], dp2[i-2] + sticker[i-2]); return Math.max(dp1[len-2], dp2[len-1]); }
- 먼저 len 길이를 설정해준다. DP를 실행할 때, 현재 인덱스의 전값, 전전값 을 확인하므로 i = 2를 시작점으로 잡기 위해 sticker.length에 2를 더해준 것이다.
- dp1, dp2 는 각각 DP를 실행하기 위한 배열로 0으로 초기화한다. 각각 첫 번째 스티커를 뜯는 / 뜯지 않는 경우에 해당한다.
- sticker가 1개인 경우, sticker[0] 만 뜯을 수 있으므로 이를 반환한다.
- 먼저, 첫 번째 스티커를 뜯는 경우(dp1)을 연산한다. dp1[2]가 sticker[0]을 뜯는 경우에 해당하며, 이 경우는 마지막 스티커를 뜯지 못하기 때문에 i가 len-1 미만까지만 확인한다.
- DP는 현재 차례가, 1) 이전 스티커를 뜯어 현재는 뜯지 않는 경우, 2) 전전 스티커까지 뜯어 현재까지 뜯는 경우, 중 최대값을 계산한다.
- 마찬가지로, 첫 번째 스티커를 뜯지 않는 경우(dp2)를 연산한다. 이 땐 마지막 스티커를 뜯을 수 있어서 i가 len 미만까지 확인한다.
- dp1, dp2의 끝값이 첫 번째 스티커를 뜯는 / 뜯지 않는 각각의 경우에 대한 최대값에 해당한다. 이 중, 큰 값을 최종 반환한다.
동적 프로그래밍의 원리 자체는 어렵지 않다. 전과 전전의 연산값을 참고해, 현재의 연산값을 누적해나가는 방법이다. (ex) 피보나치 수열)
하지만, 이 문제의 경우, 첫 번째 스티커를 뜯는지 여부에 따라 2가지 연산을 해야 한다는 점, 그리고 여기에 따라 어디까지 탐색을 하는지(len) 설정하는 과정 등이 매우 어려운 문제였다!
반응형'Algorithm' 카테고리의 다른 글
[CodeKata] 프로그래머스(Lv3) : 멀리 뛰기 (0) 2022.02.19 [CodeKata] 프로그래머스(Lv3) : 거스름돈 (0) 2022.02.13 [CodeKata] 프로그래머스(Lv3) : 숫자 게임 (0) 2022.01.30 [CodeKata] 프로그래머스: 1.23(일), 가장 긴 펠린드롬 (0) 2022.01.23 [CodeKata] 프로그래머스: 1.15(토), 스타 수열 (0) 2022.01.15