코틀린 코딩테스트 강좌, 거짓말쟁이가 되긴 싫어

문제 설명

우리 마을에는 여러 명의 주민들이 있습니다. 이 주민들 중에서 일부는 자신이 하고 싶은 말을 하기 위해 거짓말을 합니다. 과연 우리가 알고 있는 정보 중에서 거짓말쟁이를 판별할 수 있을까요?

문제 정리

주어진 N명의 주민들이 자신의 이웃들에 대한 정보를 제공합니다. 각 주민은 자신이 이웃으로 생각하는 주민들이 누구인지, 그런 주민들이 자신에 대해 어떤 말을 했는지에 대한 정보를 입력받습니다. 우리는 각 주민이 자신을 기만하고 있는지를 판별하려고 합니다. 결국 이 문제는 주민들의 진술이 서로 일치하는지를 파악하는 것입니다.

입력 형식

첫 번째 줄에 주민의 수 N이 입력됩니다. 이후 N줄에 걸쳐 각 주민의 인덱스, 그 주민이 생각하는 이웃의 수 M, 그리고 M명의 이웃에 대한 정보가 흩어져서 입력됩니다. 이웃의 정보는 각각 그 이웃의 인덱스와 해당 인덱스에 대한 진술이 거짓인지 진실인지에 대한 정보가 포함됩니다.

출력 형식

각 주민이 거짓말쟁이인지 아닌지를 판단하여, YES 또는 NO로 결과를 출력합니다.

예시 입력

    3
    0 2 1 1 0
    1 1 0
    2 1 1
    

예시 출력

    NO
    NO
    YES
    

문제 풀이 과정

이 문제를 해결하기 위해 우리는 다음과 같은 과정을 따릅니다.

1단계: 문제 이해

주민들의 발언에서 진실과 거짓을 판별하기 위해서는 각 주민이 이웃에 대해 한 말을 바탕으로 서로 다른 주민의 진술 간의 일관성을 확인해야 합니다. 예를 들어, A라는 주민이 B를 이웃이라고 주장하는 경우, B가 A를 거짓말이라고 주장하면, A가 진짜 거짓말쟁이라는 것을 알 수 있습니다.

2단계: 데이터 구조 설계

이 문제를 해결하기 위해 배열 또는 리스트를 이용하여 주민들의 정보를 저장하고 관리할 수 있습니다. 각 주민에 대한 정보를 담기 위해 각 주민의 인덱스 (0부터 시작)와 이웃의 정보를 담는 리스트를 사용하는 방법이 적절합니다. 이를 위해 Pair 클래스를 사용하여 주민 정보를 구성하는 것도 좋습니다.

3단계: 알고리즘 설계

주어진 주민들에 대한 정보를 수집한 후, 우리는 이 정보를 바탕으로 주민들의 발언이 서로 일치하는지를 판단할 것입니다. 이를 위해 다음의 알고리즘을 설계할 수 있습니다.

    1. 주민의 수 N을 입력받는다.
    2. 각 주민의 정보를 담기 위해 리스트를 초기화 한다.
    3. 주민 각각에 대하여 이웃의 정보와 진술을 입력받아 저장한다.
    4. 각 주민의 이웃의 진술이 서로 일치하는지를 체크한다.
    5. 일치하지 않는 경우 해당 주민을 거짓말쟁이로 판별한다.
    6. 결과를 출력한다.
    

4단계: 코틀린 코드 구현

이제 본격적으로 알고리즘을 코틀린 코드로 구현합니다. 아래는 해당 문제를 해결하기 위한 코틀린 코드입니다.


    data class Neighbor(val index: Int, val isLiar: Boolean)

    fun findLiar(N: Int, statements: List>): List {
        val result = MutableList(N) { "NO" }

        for (i in 0 until N) {
            val neighbors = statements[i]
            for (neighbor in neighbors) {
                val expectedTruth = !neighbor.isLiar
                if (expectedTruth && result[neighbor.index] == "YES") {
                    result[i] = "YES"
                    break
                } else if (!expectedTruth && result[neighbor.index] == "NO") {
                    result[i] = "YES"
                    break
                }
            }
        }
        return result
    }

    fun main() {
        val N = readLine()!!.toInt()
        val statements = mutableListOf>()

        for (i in 0 until N) {
            val input = readLine()!!.split(" ").map { it.toInt() }
            val M = input[1]
            val neighbors = mutableListOf()

            for (j in 0 until M) {
                val neighborIndex = input[2 + j * 2]
                val liar = input[3 + j * 2] == 1
                neighbors.add(Neighbor(neighborIndex, liar))
            }
            statements.add(neighbors)
        }

        val result = findLiar(N, statements)
        for (r in result) {
            println(r)
        }
    }
    

5단계: 테스트 및 검증

코드가 완성되었으면 다양한 테스트 케이스를 통해 코드를 검증해야 합니다. 최소 입력, 최대 입력, 다양한 시나리오를 통해 코드의 전반적인 안정성과 신뢰성을 확인할 필요가 있습니다.

결론

이번 문제를 통해 코틀린에서 알고리즘 문제를 어떻게 접근하고 해결하는지를 배웠습니다. 주민들의 진술을 분석하여 진실과 거짓을 규명하는 과정은 문제 해결에 필요한 논리적 사고를 발전시키는 데 큰 도움이 됩니다. 또한, 복잡한 문제를 단순한 데이터 구조와 알고리즘 변형을 통해 접근하는 방법을 익히게 되어 많은 도움이 되었으리라 생각합니다.

이제 여러분도 조심스럽게 거짓말쟁이를 찾아내는 알고리즘을 구현해 보시기 바랍니다!

코틀린 코딩테스트 강좌, 게임 개발하기

문제 설명

코딩 테스트에서 자주 출제되는 문제는 다양한 알고리즘을 활용해야 합니다. 이번 문제는 플레이어가 몬스터를 처치하는 게임을 기반으로 한 알고리즘 문제입니다.
주어진 몬스터의 체력과 플레이어의 공격력이 주어졌을 때, 플레이어가 몬스터를 처치하는 데 필요한 최소 공격 횟수를 계산하는 것입니다.
각각의 몬스터는 고유한 체력을 가지고 있으며, 플레이어는 매 공격마다 일정량의 피해를 줄 수 있습니다.

문제

        
        주어진 N개의 몬스터와 플레이어의 공격력 K에 대해, 각 몬스터의 체력이 주어질 때,
        플레이어가 모든 몬스터를 처치하기 위해 필요한 최소 공격 횟수를 계산하는 프로그램을 작성하세요.

        입력 형식:
        - 첫 번째 줄에는 몬스터의 수 N (1 ≤ N ≤ 10^5)과 공격력 K (1 ≤ K ≤ 10^4)가 주어집니다.
        - 두 번째 줄에는 각 몬스터의 체력 H[i] (1 ≤ H[i] ≤ 10^5) 가 N개의 정수로 주어집니다.

        출력 형식:
        - 플레이어가 몬스터를 처치하는 데 필요한 최소 공격 횟수를 출력합니다.
        
        

접근 방식

이 문제를 해결하기 위해 우리는 다음과 같은 접근 방식을 사용합니다:

  1. 먼저, 플레이어의 공격력이 몬스터의 체력에 비례하여 몬스터를 처치하는데 필요한 공격 횟수를 계산합니다.
  2. 각 몬스터에 대해 필요한 공격 횟수를 구한 후, 전체 몬스터를 처치하는 데 필요한 공격 횟수를 합산합니다.
  3. 최종적으로 계산된 공격 횟수를 출력합니다.

단계별 풀이

1단계: 입력 값 처리

입력값으로 몬스터의 수 N과 플레이어의 공격력 K를 입력받습니다.
그 다음 N개의 몬스터 체력을 배열로 입력받아야 합니다. 코틀린의 표준 입력을 사용하여 이를 처리합니다.

2단계: 각 몬스터에 대한 공격 횟수 계산

각 몬스터의 체력 H[i]에 대해, 몬스터를 처치하기 위한 공격 횟수는 (H[i] + K – 1) / K로 계산할 수 있습니다.
이는 몬스터의 체력을 플레이어의 공격력으로 나누고, 올림 처리를 하여 얻습니다.
예를 들어, 만약 H[i]가 10이고 K가 3이라면, 최소 공격 횟수는 (10 + 3 – 1) / 3 = 4입니다.

3단계: 모든 몬스터에 대한 총 공격 횟수 계산

각 몬스터의 공격 횟수를 구한 후, 이를 모두 합산하면 최종적으로 필요한 총 공격 횟수를 얻을 수 있습니다.

코드

        
        fun main() {
            // 입력 받기
            val (N, K) = readLine()!!.split(" ").map { it.toInt() }
            val healthList = readLine()!!.split(" ").map { it.toInt() }

            // 총 필요한 공격 횟수를 계산할 변수
            var totalAttacks = 0

            // 각 몬스터에 대해 필요한 공격 횟수 계산
            for (health in healthList) {
                totalAttacks += (health + K - 1) / K  // 올림 처리
            }

            // 결과 출력
            println(totalAttacks)
        }
        
    

결과

위의 코드를 실행하면 플레이어가 모든 몬스터를 처치하기 위해 필요한 최소 공격 횟수를 출력할 수 있습니다.
이 문제를 통해 몬스터의 체력과 플레이어의 공격력 간의 관계를 이해하고, 문제를 수학적 관점에서 접근하는 방법을 배울 수 있습니다.

결론

이번 코틀린 코딩테스트 강좌에서는 간단한 게임 개발 기반의 알고리즘 문제를 다루어 보았습니다.
문제를 해결하기 위해 필요한 수학적 접근과 코딩 기법을 결합함으로써, 효율적인 코드를 작성하고 최적화된 결과를 얻는 것이 중요함을 알 수 있습니다.
앞으로 더 다양한 알고리즘 문제를 접하고, 해결하는 방법을 연습한다면 더욱 전문적인 개발자로 성장할 수 있을 것입니다.

코틀린 코딩테스트 강좌, 가장 빠른 버스 노선 구하기

코틀린을 활용한 코딩테스트 강좌의 일환으로, 가장 빠른 버스 노선을 구하는 문제를 다뤄보겠습니다. 현대 사회에서 대중교통은 중요한 이동 수단이며, 특히 버스를 이용한 이동 경로 파악은 많은 사람들이 필요로 하는 기능입니다. 본 문제에서는 주어진 조건에 따라 최단 시간을 계산하는 알고리즘을 설계하고 구현해볼 것입니다.

문제 정의

다음과 같이 여러 개의 버스 노선이 주어집니다. 각 노선은 시작 정류장, 도착 정류장, 소요 시간을 포함합니다. 여러분의 목표는 특정 시작 정류장에서 특정 도착 정류장까지의 가장 빠른 소요 시간을 구하는 것입니다.

문제 입력

- 정류장의 수 N (1 ≤ N ≤ 100)
- 노선의 수 M (1 ≤ M ≤ 1000)
- 각 노선 정보: 시작 정류장 A, 도착 정류장 B, 소요 시간 T (1 ≤ A, B ≤ N, 1 ≤ T ≤ 1000)
- 시작 정류장 S와 도착 정류장 D

문제 출력

- 시작 정류장에서 도착 정류장까지 가는 가장 빠른 소요 시간. 불가능한 경우 -1을 출력.

문제 해결 전략

본 문제는 최단 경로를 찾는 문제로, 적합한 알고리즘으로는 Dijkstra의 최단 경로 알고리즘을 사용할 수 있습니다. Dijkstra 알고리즘은 가중치가 있는 그래프에서 시작 정점으로부터 다른 모든 정점까지의 최단 경로를 찾는 데 유용합니다. 버스 노선 정보를 그래프로 구축하고, 해당 그래프를 활용하여 최단 시간을 계산할 것입니다.

단계별 접근 방법

1단계: 입력 데이터 구조 정의

우선, 주어진 노선 정보를 바탕으로 그래프를 어떤 형식으로 표현할지를 결정합니다. 인접 리스트를 사용하여 각 정류장과 연결된 노선 정보를 관리하는 것이 효율적입니다.

val graph: Array>> = Array(N + 1) { mutableListOf() }

2단계: 입력 데이터 처리

사용자로부터 입력받은 노선 정보를 그래프 형태로 변환합니다. 각 노선 정보를 알맞은 형식으로 그래프에 추가합니다.

for (i in 0 until M) {
    val (A, B, T) = readLine()!!.split(" ").map { it.toInt() }
    graph[A].add(Pair(B, T))
}

3단계: Dijkstra 알고리즘 구현

Dijkstra 알고리즘을 이용하여 모든 정류장에 대한 최단 경로를 계산합니다. 우선순위 큐를 사용하여 현재 소요 시간이 가장 적은 노선을 먼저 처리합니다.

fun dijkstra(start: Int): IntArray {
    val distances = IntArray(N + 1) { Int.MAX_VALUE }
    distances[start] = 0
    val priorityQueue = PriorityQueue>(compareBy { it.second })
    priorityQueue.add(Pair(start, 0))

    while (priorityQueue.isNotEmpty()) {
        val (currentStation, currentTime) = priorityQueue.poll()

        if (currentTime > distances[currentStation]) continue

        for (edge in graph[currentStation]) {
            val (nextStation, travelTime) = edge
            val newTime = currentTime + travelTime

            if (newTime < distances[nextStation]) {
                distances[nextStation] = newTime
                priorityQueue.add(Pair(nextStation, newTime))
            }
        }
    }
    return distances
}

4단계: 결과 출력

최종적으로 시작 정류장에서 도착 정류장까지의 최단 소요 시간을 출력합니다. 만약 도착 정류장에 도달할 수 없는 경우 -1을 출력합니다.

val distances = dijkstra(S)
println(if (distances[D] == Int.MAX_VALUE) -1 else distances[D])

전체 코드 예제

fun main() {
    val (N, M) = readLine()!!.split(" ").map { it.toInt() }
    val graph: Array>> = Array(N + 1) { mutableListOf() }

    for (i in 0 until M) {
        val (A, B, T) = readLine()!!.split(" ").map { it.toInt() }
        graph[A].add(Pair(B, T))
    }

    val (S, D) = readLine()!!.split(" ").map { it.toInt() }
    val distances = dijkstra(S)

    println(if (distances[D] == Int.MAX_VALUE) -1 else distances[D])
}

fun dijkstra(start: Int): IntArray {
    val distances = IntArray(N + 1) { Int.MAX_VALUE }
    distances[start] = 0
    val priorityQueue = PriorityQueue>(compareBy { it.second })
    priorityQueue.add(Pair(start, 0))

    while (priorityQueue.isNotEmpty()) {
        val (currentStation, currentTime) = priorityQueue.poll()

        if (currentTime > distances[currentStation]) continue

        for (edge in graph[currentStation]) {
            val (nextStation, travelTime) = edge
            val newTime = currentTime + travelTime

            if (newTime < distances[nextStation]) {
                distances[nextStation] = newTime
                priorityQueue.add(Pair(nextStation, newTime))
            }
        }
    }
    return distances
}

결론

이번 강좌에서는 코틀린을 사용하여 가장 빠른 버스 노선을 선택하는 문제를 해결해 보았습니다. Dijkstra 알고리즘을 통해 최단 경로 문제를 해결하는 과정과, 문제의 여러 요소를 고려하여 코드로 구현하는 방법을 배울 수 있었습니다. 알고리즘 문제는 연습을 통해 더 나아질 수 있으므로, 지속적으로 다양한 문제를 풀어보는 것을 추천합니다.

참고: 이 문제는 그래프 이론에 대한 이해가 필요하며, Dijkstra 알고리즘 외에도 벨만-포드 알고리즘 등의 다른 최단 경로 알고리즘도 연습해 보시길 바랍니다.

코틀린 코딩테스트 강좌, 가장 큰 정사각형 찾기

안녕하세요, 코틀린을 활용한 코딩테스트 문제 풀이 강좌에 오신 것을 환영합니다. 이번 시간에는 “가장 큰 정사각형 찾기”라는 문제를 해결해보겠습니다. 이 문제는 주어진 2차원 배열에서 가장 큰 정사각형의 넓이를 찾는 과제입니다. 이 문제를 통해 동적 프로그래밍(Dynamic Programming)의 기초 개념을 익히고, 코틀린 언어의 문법과 특징도 이해할 수 있습니다.

문제 설명

주어진 m x n 크기의 2차원 이진 배열이 있습니다. 이 배열에서 ‘1’은 해당 위치에서 정사각형이 형성될 수 있음을 의미하고, ‘0’은 그렇지 않음을 의미합니다. 가장 큰 정사각형의 넓이를 찾는 것이 목표입니다.

예를 들어, 다음과 같은 배열이 있을 때:

    1 0 1 0 0
    1 0 1 1 1
    1 1 1 1 1
    1 0 0 1 0
    

이 경우, 가장 큰 정사각형의 넓이는 4입니다. (2×2 정사각형)

문제 접근 방법

이 문제를 해결하기 위해 우리는 동적 프로그래밍을 사용할 것입니다. 다음은 문제를 해결하기 위한 주요 단계입니다:

  1. DP 배열 초기화: 입력받은 2차원 배열과 같은 크기를 가지는 DP 배열을 생성합니다. 이 DP 배열의 각 원소는 (i, j) 위치에서 끝나는 정사각형의 한 변의 길이를 나타냅니다.
  2. 상태 전이 관계 설정: 만약 grid[i][j]가 ‘1’이라면, DP[i][j]는 DP[i-1][j], DP[i][j-1], DP[i-1][j-1] 중 최소값 + 1이 됩니다. 이는 (i,j)에서의 가장 큰 정사각형의 변 길이를 결정합니다.
  3. 최대 정사각형 변 길이 추적: 이 과정을 통해 가장 큰 정사각형의 변 길이를 추적하고, 마지막에 그 값을 제곱하여 넓이를 계산합니다.

코드 구현

이제 위의 알고리즘을 코틀린으로 구현해보겠습니다. 아래는 해당 알고리즘의 코드입니다:


    fun maximalSquare(matrix: Array): Int {
        if (matrix.isEmpty()) return 0

        val m = matrix.size
        val n = matrix[0].size
        val dp = Array(m) { IntArray(n) }
        var maxSide = 0

        for (i in 0 until m) {
            for (j in 0 until n) {
                if (matrix[i][j] == '1') {
                    if (i == 0 || j == 0) {
                        dp[i][j] = 1 // 첫 행이나 첫 열의 경우
                    } else {
                        dp[i][j] = minOf(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) + 1
                    }
                    maxSide = maxOf(maxSide, dp[i][j])
                }
            }
        }
        return maxSide * maxSide // 정사각형의 넓이 반환
    }
    

코드 설명

코드는 2차원 배열을 입력으로 받아, 각 셀에 대해 해당 위치에서 끝나는 정사각형의 한 변의 길이를 계산합니다. 다음은 코드의 주요 부분에 대한 설명입니다:

  • 초기 검증: 입력된 행렬이 비어있는지 검사합니다. 비어있다면 0을 반환합니다.
  • 이중 루프: 각 원소를 순회하며, ‘1’일 경우 DP 테이블에 값을 업데이트합니다.
  • 최대 사이드 업데이트: 각 경우 최댓값을 추적하여, 마지막에 이를 제곱하여 결과를 반환합니다.

복잡도 분석

이 알고리즘의 시간 복잡도는 O(m * n)이며, DP 배열을 사용하므로 공간 복잡도 또한 O(m * n)입니다. 하지만 공간을 최적화할 수도 있습니다. DP 배열 대신 두 개의 변수를 사용하여 현재 행과 이전 행의 정보를 저장함으로써 공간 복잡도를 O(n)으로 줄일 수 있습니다.

선택적 공간 최적화 코드


    fun maximalSquare(matrix: Array): Int {
        if (matrix.isEmpty()) return 0
        val m = matrix.size
        val n = matrix[0].size
        val dp = IntArray(n)
        var maxSide = 0
        var prev = 0

        for (i in 0 until m) {
            for (j in 0 until n) {
                val temp = dp[j]
                if (matrix[i][j] == '1') {
                    dp[j] = if (i == 0 || j == 0) 1 else minOf(dp[j], dp[j - 1], prev) + 1
                    maxSide = maxOf(maxSide, dp[j])
                } else {
                    dp[j] = 0
                }
                prev = temp
            }
        }
        return maxSide * maxSide
    }
    

결론

이번 강좌를 통해 “가장 큰 정사각형 찾기” 문제를 해결하는 방법을 학습했습니다. 이 과정에서 동적 프로그래밍의 기본적인 개념을 이해하고, 코틀린 언어로 알고리즘을 구현하는 방법을 배웠습니다. 이러한 알고리즘 문제를 통해 문제 해결 능력을 향상시키고, 코딩 테스트 준비에 큰 도움이 될 것입니다. 다음 시간에는 또 다른 흥미로운 알고리즘 문제를 다뤄보겠습니다. 감사합니다!

코틀린 코딩테스트 강좌, 가장 길게 증가하는 부분 수열 찾기

안녕하세요! 이번 포스트에서는 코딩 테스트에서 자주 출제되는 “가장 길게 증가하는 부분 수열” 문제를 다뤄보겠습니다. 이 문제는 알고리즘의 기초를 이해하고, 실력을 쌓는 데 큰 도움이 됩니다. 문제를 효과적으로 풀기 위한 이론과 코틀린을 활용한 구체적인 구현 과정을 설명하겠습니다.

문제 설명

주어진 배열에서 가장 길게 증가하는 부분 수열(LIS, Longest Increasing Subsequence)을 찾는 문제입니다. 부분 수열이란, 원래 배열의 원소를 순서대로 선택해서 만든 배열이며, 원래 배열의 순서를 유지해야 합니다. 증가 수열이란 원소들이 모두 오름차순으로 정렬되어 있는 수열을 의미합니다.

예제

    입력: [10, 22, 9, 33, 21, 50, 41, 60, 80]
    출력: 6
    설명: 가장 길게 증가하는 부분 수열의 예는 [10, 22, 33, 50, 60, 80]입니다.
    

문제 풀이 접근법

이 문제를 해결하기 위한 기본적인 접근법은 동적 프로그래밍(DP)을 사용하는 것입니다. DP는 문제를 작은 부분 문제로 나누어 푸는 방법이며, 이전의 계산 결과를 저장하여 같은 계산을 반복하지 않도록 합니다.

1단계: DP 테이블 초기화

먼저 DP 테이블을 초기화합니다. 입력 배열의 각 인덱스에서 시작할 때의 LIS 길이를 저장할 배열을 생성합니다. 초기에는 각 원소가 자기 자신으로 이루어진 부분 수열이 최대이므로 모든 값은 1로 설정합니다.

2단계: DP 테이블 업데이트

이제 입력 배열의 모든 원소에 대해 서로 비교합니다. 각 원소 arr[i]에 대해, 그보다 앞에 있는 모든 원소 arr[j] (j < i)를 확인하여 다음 조건을 평가합니다:

    if arr[i] > arr[j] then
        dp[i] = max(dp[i], dp[j] + 1)
    

여기서 dp[i]arr[i]를 포함하는 LIS의 길이이며, dp[j] + 1arr[j] 다음에 arr[i]를 추가한 경우의 길이를 나타냅니다.

3단계: 결과 도출

모든 원소에 대한 처리가 끝난 후, dp 배열에서 가장 큰 값을 찾아 LIS의 길이를 결과로 반환합니다.

코틀린 구현

이제 직접 코틀린으로 구현해보겠습니다. 아래는 앞서 설명한 알고리즘을 따르는 코드입니다.


fun longestIncreasingSubsequence(arr: IntArray): Int {
    val n = arr.size
    val dp = IntArray(n) { 1 } // initialize dp array

    for (i in 1 until n) {
        for (j in 0 until i) {
            if (arr[i] > arr[j]) {
                dp[i] = maxOf(dp[i], dp[j] + 1)
            }
        }
    }
    return dp.maxOrNull() ?: 1 // return the maximum value in dp array
}

fun main() {
    val arr = intArrayOf(10, 22, 9, 33, 21, 50, 41, 60, 80)
    val length = longestIncreasingSubsequence(arr)
    println("가장 길게 증가하는 부분 수열의 길이: $length")
}
    

복잡도 분석

결과적으로, 이 알고리즘의 시간 복잡도는 O(n²)입니다. 이는 중첩된 반복문이 n에 대해 실행되기 때문입니다. 공간 복잡도는 O(n)으로 DP 배열을 저장하기 위한 공간이 필요합니다.

최적화 방법

만약 더 높은 성능이 요구된다면, 이진 탐색(binary search)을 활용하여 LIS 문제를 O(n log n)으로 해결할 수 있습니다. 이 방법은 다음과 같은 방식으로 구현됩니다:


import java.util.*

fun longestIncreasingSubsequenceOptimized(arr: IntArray): Int {
    if (arr.isEmpty()) return 0

    val tails = mutableListOf()

    for (num in arr) {
        val index = tails.binarySearch(num)
        if (index < 0) {
            val pos = -(index + 1)
            if (pos == tails.size) {
                tails.add(num)
            } else {
                tails[pos] = num
            }
        }
    }
    return tails.size
}
    

여기서는 tails 배열이 LIS의 가능한 마지막 요소들을 저장합니다. 이진 탐색을 통해 적절한 위치를 찾아 삽입하거나 교체합니다.

마무리

이번 글에서는 코틀린을 이용한 가장 길게 증가하는 부분 수열 문제에 대해 알아보았습니다. 문제의 접근 방식과 구현 방법을 자세히 설명드렸고, 추가적으로 최적화 방법도 다뤘습니다. 이러한 문제들은 코딩 테스트에서 자주 출제되므로, 반복적인 연습이 중요합니다. 앞으로도 다양한 알고리즘 문제를 함께 나누도록 하겠습니다. 궁금한 점이 있다면 댓글로 질문해 주세요!