Skip to content

[재영] 인사고과, 연속 펄스 부분 수열의 합 #106

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions 황재영/연속 펄스 부분 수열의 합.md
Original file line number Diff line number Diff line change
@@ -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
}
}
```
49 changes: 49 additions & 0 deletions 황재영/인사고과.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# 인사고과

- 풀이 시간: 1시간 🥹 (코틀린 문법 어려워요...)
- 알고리즘: 정렬

## 풀이 방법

1. 핵심은 정렬에서 인덱스를 구하되, 어떻게 **인센티브 제외대상을 필터링할 것이냐**에요.
2. 따라서 인센티브 제외대상인, 둘 다 타 사원 점수 대비 낮은 사원을 거르기 위해 내림차순 / 오름차순으로 정렬했어요. 이렇게 하면, 추후 내림차순을 통해 첫 번째 인덱스 점수는 거를 수 있고, 두 번째 인덱스 점수가 현재 가장 max인 것 대비 작은지만 판단하면 돼요.
3. 추가로 완호 역시 이 과정 중 인센티브 제외대상인지 판단해주고, 이에 따라 `isNotIncentive`를 업데이트해요.
4. 이후 필터링된 배열에서 점수의 합을 구하고, 인덱스를 구해주면 돼요.

```kt
class Solution {
fun solution(scores: Array<IntArray>): Int {
val (score1, score2) = scores[0]

val total = score1 + score2

var cutline = 0

val sortedScores = scores.sortedWith(compareByDescending<IntArray> { 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
};
}
}
```