diff --git "a/\355\231\251\354\236\254\354\230\201/\354\227\260\354\206\215 \355\216\204\354\212\244 \353\266\200\353\266\204 \354\210\230\354\227\264\354\235\230 \355\225\251.md" "b/\355\231\251\354\236\254\354\230\201/\354\227\260\354\206\215 \355\216\204\354\212\244 \353\266\200\353\266\204 \354\210\230\354\227\264\354\235\230 \355\225\251.md" new file mode 100644 index 0000000..d730ed7 --- /dev/null +++ "b/\355\231\251\354\236\254\354\230\201/\354\227\260\354\206\215 \355\216\204\354\212\244 \353\266\200\353\266\204 \354\210\230\354\227\264\354\235\230 \355\225\251.md" @@ -0,0 +1,66 @@ +# 연속 펄스 부분 수열의 합 + +- 풀이 시간: 15분 +- 사용 알고리즘: DP + +## 풀이 방법 + +1. 일단 **어떻게 이 문제의 최대 값을 찾아낼지**에 주목했어요. 그 결과 다음과 같은 후보군이 떠올랐어요. + +> - 투 포인터를 이용해서 오른쪽으로 계속 포인터를 이동하면서 최댓값을 찾아낸다. +> - DP를 이용해서 이전의 최댓값을 캐싱해서 찾아낸다. +> - 그리디하게 최적의 해를 찾아내는 방법을 구한다. + +이중 저는 2번째인 **DP**를 택했어요. + +이유는 투 포인터의 경우 결국 투 포인터를 이동하는 기준을 찾아야 하는데, 왼쪽 포인터를 이동해야 할 기준을 생각하기 마땅치 않았어요. + +그리디 역시 마찬가지였어요. 탐욕적으로 해를 구하는 방법에는 예외가 많아 보였어요. 예컨대, 4 -1 -1 -6이라 하면 10이 나와야 하는데, 그리디적으로 풀기에는 사이에 작은 음수값이 있을 때(펄스 수열의 곱으로 변환한 4, 1, -1, 6을 탐색할 때)에 대한 변수를 어떻게 풀어나가야 할지 막막했어요. + +따라서 이전의 최댓값을 기반으로 계속해서 최댓값을 업데이트하며 메모이제이션하는 DP를 택했어요. + +### 점화식 + +그렇다면 DP를 적용할 때는 어떻게 점화식을 만들 것인가를 고민해야 하는데요. + +- i번째 단계에서의 최댓값을 Result +- `[1, -1, ...]`의 펄스 수열을 곱한 값과 해당 인덱스에서의 최댓값을 저장하는 배열을 v1, DP1 +- `[-1, 1, ...]`의 펄스 수열을 곱한 값과 해당 인덱스에서의 최댓값을 저장하는 배열을 v2, DP2라 한다면 + +> Result[i] = max(max(DP1[i - 1] + v1[i], v1[i]), max(DP2[i - 1] + v2[i], v2[i])) + +어떤 단계에서든 최댓값은 나올 수 있기 때문에, 결국 Result 배열을 다 순회하면 문제를 해결할 수 있어요. (저는 여기서, DP 결과를 각각 계산하는 방식으로 했지요!) + +```kt +import kotlin.math.* + +class Solution { + fun solution(sequence: IntArray): Long { + var answer: Long = 0 + + val DP1 = LongArray(sequence.size) + val DP2 = LongArray(sequence.size) + + sequence.forEachIndexed { index, v -> + val flag = if (index % 2 == 0) 1 else -1 + + val now1 = flag * v.toLong() + val now2 = now1 * -1 + + if (index == 0) { + DP1[0] = now1 + DP2[0] = now2 + } else { + DP1[index] = maxOf(now1, DP1[index - 1] + now1) + DP2[index] = maxOf(now2, DP2[index - 1] + now2) + } + } + + for (i in 0 until sequence.size) { + answer = maxOf(answer, DP1[i], DP2[i]) + } + + return answer + } +} +``` diff --git "a/\355\231\251\354\236\254\354\230\201/\354\235\270\354\202\254\352\263\240\352\263\274.md" "b/\355\231\251\354\236\254\354\230\201/\354\235\270\354\202\254\352\263\240\352\263\274.md" new file mode 100644 index 0000000..5f83543 --- /dev/null +++ "b/\355\231\251\354\236\254\354\230\201/\354\235\270\354\202\254\352\263\240\352\263\274.md" @@ -0,0 +1,49 @@ +# 인사고과 + +- 풀이 시간: 1시간 🥹 (코틀린 문법 어려워요...) +- 알고리즘: 정렬 + +## 풀이 방법 + +1. 핵심은 정렬에서 인덱스를 구하되, 어떻게 **인센티브 제외대상을 필터링할 것이냐**에요. +2. 따라서 인센티브 제외대상인, 둘 다 타 사원 점수 대비 낮은 사원을 거르기 위해 내림차순 / 오름차순으로 정렬했어요. 이렇게 하면, 추후 내림차순을 통해 첫 번째 인덱스 점수는 거를 수 있고, 두 번째 인덱스 점수가 현재 가장 max인 것 대비 작은지만 판단하면 돼요. +3. 추가로 완호 역시 이 과정 중 인센티브 제외대상인지 판단해주고, 이에 따라 `isNotIncentive`를 업데이트해요. +4. 이후 필터링된 배열에서 점수의 합을 구하고, 인덱스를 구해주면 돼요. + +```kt +class Solution { + fun solution(scores: Array): Int { + val (score1, score2) = scores[0] + + val total = score1 + score2 + + var cutline = 0 + + val sortedScores = scores.sortedWith(compareByDescending { it[0] } + .thenBy { it[1] }) + + var isNotIncentive = false; + + val filteredIncentiveReceivers = sortedScores.filter { (s1, s2) -> + if (score1 < s1 && score2 < s2) { + isNotIncentive = true; + } + + if (s2 >= cutline) { + cutline = s2 + + true + } else { + s2 >= cutline + } + }.map { it.sum() } + + return if (isNotIncentive) { + -1 + } else { + val ranks = filteredIncentiveReceivers.sortedDescending() + ranks.indexOf(total) + 1 + }; + } +} +```