Kotlin/Algorithm Problems

<프로그래머스> 광물 캐기(Lv.2)

re트 2024. 1. 30. 11:05
728x90

[깃허브]

https://github.com/heesoo-park/ForCodeKata/tree/main/%EA%B4%91%EB%AC%BC%20%EC%BA%90%EA%B8%B0

[프로그래머스]

https://school.programmers.co.kr/learn/courses/30/lessons/172927

제출 결과

 

이전에 한번 풀려고 했다가 중도하차한 다음에 오늘 다시 도전했다.

그 때보다는 확실히 몸이 좋아서 그런지 문제에 집중이 잘 됐다.

물론 그렇다고 쉽게 풀린 건 아니었다.

 

가장 먼저 한 것은 입력으로 들어온 광물들을 Int타입으로 바꾸고 5개씩 나누는 작업을 했다.

그걸 위해서 key가 String이고 value가 Int인 해쉬맵을 만들어 map 안에서 사용했고 chunked 함수를 통해 5개씩 나눠주었다.

그랬더니 List<List<Int?>> 타입이 되었다.

그리고 해당 리스트를 순회하면서 가장 작은 값, 다이아몬드 > 철 > 돌 순으로 찾아 피로도를 계산해주었다.

다이아몬드가 있을 때는 다이아몬드 곡괭이 > 철 곡괭이 > 돌 곡괭이 순으로,

철과 돌만 있을 때는 철 곡괭이 > 다이아몬드 곡괭이 > 돌 곡괭이 순으로,

돌만 있을 때는 돌 곡괭이 > 철 곡괭이 > 다이아몬드 곡괭이 순으로

사용할 수 있게 세팅했다.

 

피로도 계산은 sumOf를 사용했고 그 안에서 문제에서 주어진 표를 변수로 만든 걸 가지고 곡괭이에 맞는 광물의 피로도를 변환해줬다.

이렇게 하고 제출했더니 틀렸다.

 

사실 제출할 때부터 틀릴 걸 직감했었다.

왜냐하면 내가 짠 알고리즘은 앞에서 뒤로 가는 단방향이었기 때문이다.

중간에 더 좋은 곡괭이를 써야하는 케이스를 커버하지 못하기 때문이다.

이걸 틀리고나서부터 고민하기 시작했다...ㅎㅎ

 

그래서 나온 건 광물들을 캘 수 있는 만큼만 잘라서 가지고 있고 조건에 맞춰서 정렬하자는 거였다.

어짜피 앞에서부터 차례대로만 광물을 가져갈 수 있는데 모든 광물들을 5개씩 잘라서 저장해놓을 필요가 없는거였다.

곡괭이의 개수만큼만 챙겼다.

물론 이 부분에서 런타임에러가 생겼었는데 이건 곡괭이가 잘라놓은 광물들의 개수보다 커서 그런 거였다.

그건 let 스코프 함수를 이용해 현재 광물들의 개수와 비교한 후 자르는 범위를 정하는 것으로 해결했다.

이제 쓸 것만 남았으니 마음대로 리스트를 움직여도 괜찮았다.

더욱이 다이아몬드, 철, 돌 곡괭이 순으로 사용할 수 있게 정렬하는 것이 좋아보이는 상황이 됐다!

그래서 sortedWith을 사용했다.

여러가지 조건을 추가해야했기 때문이다.

다이아몬드 개수 내림차순으로 정렬하는데 같으면 철 개수 내림차순으로 정렬하고, 같으면 돌 개수 내림차순으로 정렬하는 비교 조건을 넣었다.

 

이렇게 하고 나니까 이전에 엄청 길었던 when을 사용하지 않아도 됐고 그냥 차례대로 리스트를 순회하면서 다이아몬드 곡괭이가 다 떨어질때까지 다이아몬드 곡괭이를 쓰고 그 다음에 철 곡괭이 떨어질 때까지 철 곡괭이 쓰고 그 다음에 돌 곡괭이 떨어질 때까지 돌 곡괭이 쓰고 피로도 계산한 결과를 넘겨주기만 하면 됐다.

당연히 제출하니까 통과했다.

 

이 문제에서 가장 중요했던 포인트는 정렬이었던 거 같다.

사실 앞에서부터 쭉~ 가야한다는 문제 조건 때문에 생각지 못한 부분이었는데 사용할만큼만 리스트를 구성하면 정렬을 해도 상관없다는 게 나에게 놀라운 깨달음이었다.

 

해당 방법으로 작성한 코드는 다음과 같다.

class Solution {
    private val table = arrayOf(arrayOf(1, 1, 1), arrayOf(5, 1, 1), arrayOf(25, 5, 1))
    private val tier = hashMapOf("diamond" to 1, "iron" to 2, "stone" to 3)
    fun solution(picks: IntArray, minerals: Array<String>): Int {
        var answer = 0
        val blockList = minerals.map { tier[it] }.chunked(5).let { list ->
            list.subList(0, if (list.size > picks.sumOf { it }) picks.sumOf { it } else list.size).sortedWith(
                compareBy({ dia -> -dia.count { it == 1 } }, { iron -> -iron.count { it == 2 } }, { stone -> -stone.count { it == 3 } })
            )
        }

        blockList.forEach { block ->
            if (picks[0] != 0) {
                answer += calTiredness(block, 0)
                picks[0]--
            } else if (picks[1] != 0) {
                answer += calTiredness(block, 1)
                picks[1]--
            } else {
                answer += calTiredness(block, 2)
                picks[2]--
            }
        }

        return answer
    }

    private fun calTiredness(block: List<Int?>, num: Int): Int {
        return block.sumOf { table[num][it?.minus(1)!!] }
    }
}
반응형