Skip to main content

Command Palette

Search for a command to run...

[LeetCode] 2310. Sum of Numbers With Units Digit K

Updated
3 min read

Link : 2310. Sum of Numbers With Units Digit K

문제 정의

Given two integers num and k, consider a set of positive integers with the following properties:

  • The units digit of each integer is k.

  • The sum of the integers is num.

Return the minimum possible size of such a set, or -1 if no such set exists.

Note:

  • The set can contain multiple instances of the same integer, and the sum of an empty set is considered 0.

  • The units digit of a number is the rightmost digit of the number

문제 한문장 요약

일의 자리수가 k인 최소 수열의 합이 num이 될 때, 해당 수열의 최소 개수를 구해라

문제 분석

i = 우리가 구하고자 하는 최소 수열의 개수

$$num = k + (10 × 1 + k) + (10 × 2+ k) + …$$

$$num = k * i + (10 + 20 + 30 + … + 10 * (i - 1))$$

$$(num - k * i) \equiv 0 \bmod 10$$

우리가 구하고자 하는 최수 수열의 개수는 명확하게 정해져있고 수학공식을 통해서 num을 확인할 수 있는 확인 공식이 만들어졌기 때문에 이제 전체탐색을 통해서 값의 결과를 찾을 수 있습니다.

다행히 문제에서 제공되는 입력 값의 범위가 0 <= num <= 3000 이기 때문에 전체탐색을 해도 무리가 없을 것으로 판단됩니다.

해결 방법

1차 시도

처음에는 문제를 자세하게 분석하지 않고 다음과 같이 시도를 했습니다.

  1. 일의 자리가 k인 모든 수를 뽑는다

  2. 뽑은 수를 이용하여 dfs로 탐색한다

import kotlin.math.min


class Solution {
    var answer: Int = Int.MAX_VALUE

    fun minimumNumbers(num: Int, k: Int): Int {
        if (num == 0) return 0

        val list = mutableListOf<Int>()

        for (i in 1..num) {
            val mod = i % 10

            if (mod == k) {
                list.add(i)
            }
        }

        if (list.isEmpty() && num < k) return -1
        if (list.isEmpty()) return 0

        val visited = BooleanArray(list.size) { false }

        dfs(visited, list, num, 0)

        return if (Int.MAX_VALUE == answer) -1 else answer
    }

    fun dfs(visited: BooleanArray, list: List<Int>, target: Int, sum: Int) {
        for (i in list.indices) {
            if (visited[i]) continue
            visited[i] = true
            val curSum = sum + list[i]

            if (target == curSum) {
                answer = min(visited.count { it }, answer)
                visited[i] = false
                break // 여기서 종료
            } else {
                dfs(visited, list, target, curSum)
                visited[i] = false
            }
        }
    }
}

fun main() {
    val solution = Solution()
    val answer = solution.minimumNumbers(58, 9)
    println(answer)
}

하지만 해당 코드는 정답이 되지 못했습니다.

왜냐하면 해당 문제를 풀 수 있는지 없는지를 정확하게 판단하지 못했고 문제 풀이의 복잡성이 증가하여 어느 순간부터 코드를 수정할 수 없는 수준까지 이르렀기 때문입니다.

2차 시도

class Solution {
    fun minimumNumbers(num: Int, k: Int): Int {
        if (num == 0) return 0

        for (i in 1..10) {
            val total = i * k 
            if (total > num) continue
            if ((num - total) % 10 == 0) {
                return i
            }
        }

        return -1
    }
}

이번에는 문제를 분석한 것을 바탕으로 수식으로 풀어보았습니다

$$(num - k * i) \equiv 0 \bmod 10$$

느낀점

코드를 작성하면서 문제만 읽고 그대로 푸는 것이 아닌 수학적으로 분석하고 새로운 공식을 도출하는 부분이 해당 문제의 가장 어려운 부분이 아닐까 싶습니다. 해답 자체는 굉장히 짧고 단순하지만 코드하나하나에 수학적인 이유가 들어가 있는 부분은 이 문제를 얼마나 많이 고민하고 깔끔하고 정리하냐에 따라서 확실히 달라지는 부분으로 보입니다.

6 views

More from this blog

JVM은 컨테이너의 CPU와 메모리 한계를 어떻게 알아낼까

8코어 노드에 컨테이너를 띄웠는데 ForkJoinPool이 스레드를 한두 개만 만들어요. 메모리는 넉넉히 줬는데 컨테이너가 자꾸 OOMKilled로 죽고요. 분명히 같은 JAR인데 로컬에서는 멀쩡하다가 쿠버네티스에만 올리면 이상해져요. 이 글은 "왜 컨테이너 속 JVM은 다르게 행동하는가"를 cgroup이라는 진짜 경계선과, JVM이 그 경계를 읽어내는 내

May 21, 202615 min read

ThreadPoolExecutor는 언제 스레드를 새로 만들까 — execute()의 3단계

Executors.newFixedThreadPool(10) 한 줄을 쓰면서도, 11번째 작업이 오면 스레드가 11개로 늘어날 거라고 막연히 기대해 본 적 없으신가요. 실제로는 큐가 먼저 무한히 쌓이고 스레드는 영원히 10개에 머물러요. 이 글은 ThreadPoolExecutor가 작업을 받았을 때 "스레드를 새로 만들지, 큐에 넣을지, 거부할지"를 결정하는

May 21, 202617 min read

자바 synchronized는 어떻게 동작할까 — 모니터, 락 인플레이션, 그리고 사라진 biased locking

synchronized 키워드 하나로 스레드 안전을 얻는 동안, JVM 안에서는 객체 헤더의 비트를 뒤집고, 스택에 락 레코드를 쌓고, 경합이 생기면 네이티브 모니터로 승격하는 일이 벌어져요. 이 글은 그 한 번의 잠금이 객체 헤더부터 ObjectMonitor까지 어떤 경로를 거치는지, 그리고 한때 있었다가 JDK 18에서 사라진 biased locking

May 19, 202616 min read

JVM 객체 할당의 비밀 — TLAB, Bump-the-Pointer, 그리고 할당이 거의 공짜인 이유

Java에서 new를 호출하면 무슨 일이 벌어질까요? "힙에 메모리를 잡는다"는 한 문장 뒤에는 스레드마다 자기만의 분양 구역을 나눠 갖는 정교한 설계가 숨어 있어요. 이 글은 HotSpot JVM이 객체 할당을 어떻게 "거의 공짜"로 만드는지 그 내부를 따라가 보려는 글이에요. JVM 메모리 동작 원리에 관심 있는 분께 권해요. 자바를 쓰다 보면 객체를

May 15, 202614 min read

Java Zero-Copy — FileChannel.transferTo, sendfile, 그리고 Kafka가 디스크를 네트워크로 흘려보내는 방법

"파일을 읽어서 소켓으로 보낸다." 한 줄짜리 요구사항이에요. 그런데 이 한 줄 뒤에서 데이터는 메모리를 네 번이나 복사하고, CPU는 커널과 유저 공간을 네 번이나 들락거려요. Kafka처럼 초당 수십만 건을 흘려보내야 하는 시스템에서 이 비용은 그냥 넘길 수가 없어요. 이 글은 그 복사를 한 겹씩 벗겨내는 zero-copy의 동작 원리를 따라가요. 전통

May 15, 202617 min read

끄적끄적 테크 블로그

165 posts

물류 회사에 다니고 있는 개발자 블로그입니다. 개발을 너무 좋아해서 정신없이 작업하다가 중간에 끄적거리며 내용들을 몇개 적어봅니다 ㅎㅎ