문제
점심시간에 도둑이 들어, 일부 학생이 체육복을 도난당했습니다. 다행히 여벌 체육복이 있는 학생이 이들에게 체육복을 빌려주려 합니다. 학생들의 번호는 체격 순으로 매겨져 있어, 바로 앞번호의 학생이나 바로 뒷번호의 학생에게만 체육복을 빌려줄 수 있습니다. 예를 들어, 4번 학생은 3번 학생이나 5번 학생에게만 체육복을 빌려줄 수 있습니다. 체육복이 없으면 수업을 들을 수 없기 때문에 체육복을 적절히 빌려 최대한 많은 학생이 체육수업을 들어야 합니다.
전체 학생의 수 n, 체육복을 도난당한 학생들의 번호가 담긴 배열 lost, 여벌의 체육복을 가져온 학생들의 번호가 담긴 배열 reserve가 매개변수로 주어질 때, 체육수업을 들을 수 있는 학생의 최댓값을 return 하도록 solution 함수를 작성해주세요.
제한사항
- 전체 학생의 수는 2명 이상 30명 이하입니다.
- 체육복을 도난당한 학생의 수는 1명 이상 n명 이하이고 중복되는 번호는 없습니다.
- 여벌의 체육복을 가져온 학생의 수는 1명 이상 n명 이하이고 중복되는 번호는 없습니다.
- 여벌 체육복이 있는 학생만 다른 학생에게 체육복을 빌려줄 수 있습니다.
- 여벌 체육복을 가져온 학생이 체육복을 도난당했을 수 있습니다. 이때 이 학생은 체육복을 하나만 도난당했다고 가정하며, 남은 체육복이 하나이기에 다른 학생에게는 체육복을 빌려줄 수 없습니다.
입출력 예
풀이방법
1. 전체의 학생 수에서 체육복을 도난 당한 학생 수를 빼서 현재 체육복을 가지고 있는 학생 수를 구한다.
var answer = n - lost.size
2. 체육복을 도난 당한 학생 수 배열을 오름차순 정렬한다.
var sortedLost = lost.sorted()
3. map을 만들어서 여벌 체육복이 있는 학생들만큼 반복문을 돌며 현재 가지고 있는 여벌 체육복의 수를 value에 넣는다.
var map = HashMap<Int, Int>()
for(r in reserve){
map[r] = map.getOrDefault(r, 0) + 1
}
for(e in 0..n+1){
map[e] = map.getOrDefault(e, 0)
}
4. "여벌 체육복을 가져온 학생이 체육복을 도난당했을 수 있습니다."라고 했기 때문에 sortedLost에서 현재 내가 체육복을 도난 당했는데 여벌 체육복이 있는지(MeList) , 체육복을 도난 당했는데 여벌 체육복이 없는지(noClothesList) 를 각각 배열을 만들어서 넣어줬다.
그리고 '체육복을 도난 당했는데 여벌 체육복이 있다'면 내가 체육복을 입어야하기 때문에 map에서 내가 갖고 있는 체육복 - 1 을 해준다. 그리고 answer+1을 한다.
마지막으로 '체육복을 도난 당했는데 여벌 체육복이 없는' 학생들을 상대로 체육복을 빌릴 수 있는지 없는지 체크해야하기 때문에 sortedLost에 noClothesList를 할당한다.
val noClothesList = sortedLost.filter { !reserve.contains(it) }
val MeList = sortedLost.filter { reserve.contains(it) }
for(m in MeList){
map[m] = map.getOrDefault(m, 0) - 1
answer++
}
sortedLost = noClothesList
5. sortedLost를 반복하며 만약 현재 학생의 앞 학생이 체육복을 가지고 있다면(map[it-1]!! > 0) 그 학생의 체육복 수 - 1을 하고 answer를 증가시킨다.
만약 현재 학생의 뒷 학생이 체육복을 가지고 있다면(map[it+1]!! > 0) 그 학생의 체육복 수 -1을 하고 answer를 증가시킨다.
sortedLost.forEach {
if(map[it-1]!! > 0){
map[it-1] = map.getOrDefault(it-1, 0) - 1
answer++
}else if(map[it+1]!! > 0){
map[it+1] = map.getOrDefault(it+1, 0) - 1
answer++
}
}
주의할 점
내가 생각했을 때 이 문제에서 조심해야할 부분이 몇 가지 있다.
1. (풀이방법 5번)
lost를 오름차순 정렬로 했다면 조건문을 적을 때 무조건 앞 사람 부터 체크하고, 그 다음에 뒷 사람을 체크해줘야한다.
만약 이렇게 하지 않는다면 아래의 예시에서 반례가 된다.
n = 5
lost = [4, 2]
resverse = [3, 5]
return = 5
만약 아래와 같이 체육복을 입은 학생 수가 오름차순 정렬이 아닌 마구잡이로 되어있는 상태에서 체육복을 빌려줄 수 있는지 체크한다면
4번 학생은 3번 학생에게 빌릴 것이다. 그리고 2번 학생은 1 or 3번 학생에게 빌려야되는데 빌릴 수 없으므로 체육 수업에 참여할 수 있는 학생은 총 4명이다.
하지만 여기서 2,4로 오름차순 정렬한다면
2번 학생이 3번 학생에게, 4번 학생이 5번 학생에게 체육복을 빌려서 총 5명의 학생이 체육 수업에 참여할 수 있다.
정렬을 하지 않고도 어떻게든 풀 수 있겠지만 그럼 코드가 복잡해지고 무조건 어떤 반례에 걸릴 것 같아서 위와 같이 정렬을 하면 좋을 것 같다.
코드
class Solution {
fun solution(n: Int, lost: IntArray, reserve: IntArray): Int {
var answer = n - lost.size
var sortedLost = lost.sorted()
//체육복을 가지고 있는지 없는지
var map = HashMap<Int, Int>()
for(r in reserve){
map[r] = map.getOrDefault(r, 0) + 1
}
for(e in 0..n+1){
map[e] = map.getOrDefault(e, 0)
}
//여벌 체육복을 가져온 학생이 체육복 도난당했을 경우 처리
val noClothesList = sortedLost.filter { !reserve.contains(it) }
val MeList = sortedLost.filter { reserve.contains(it) }
for(m in MeList){
map[m] = map.getOrDefault(m, 0) - 1
answer++
}
sortedLost = noClothesList
//체육복을 빌릴 수 있는지 체크
sortedLost.forEach {
if(map[it-1]!! > 0){
map[it-1] = map.getOrDefault(it-1, 0) - 1
answer++
}else if(map[it+1]!! > 0){
map[it+1] = map.getOrDefault(it+1, 0) - 1
answer++
}
}
return answer
}
}
느낀점
너무 고난과 고통을 겪었던 문제였다. 생각할게 너무 많았던 문제 .. 나만 그랬나 ? 무려 Lv.1이긴 한데 .. ^^
아직 코틀린 문법이 익숙하지 않다는 것을 느꼈고 코틀린 만의 장점인 filter와 같은 함수를 적극 사용해야겠다.
https://school.programmers.co.kr/learn/courses/30/lessons/42862?language=kotlin#
'Algorithm > kotlin' 카테고리의 다른 글
[프로그래머스/Kotlin] 문자열 내림차순으로 정렬하기 (Lv. 0) (0) | 2024.04.08 |
---|---|
[프로그래머스/Kotlin] 바탕화면 정리 (Lv. 1) (0) | 2024.04.05 |
[프로그래머스/Kotlin] 덧칠하기 (Lv. 1) (0) | 2024.04.03 |
[프로그래머스/Kotlin] N개의 최소공배수 (Lv. 2) (0) | 2024.03.29 |
[프로그래머스/Kotlin] 공원 산책 (Lv. 1) (2) | 2024.03.29 |