본문 바로가기
코딩테스트

[백준] 10813 공 바꾸기 - Kotlin(코틀린)

by Glion 2025. 4. 15.
반응형

10813 공 바꾸기

 

이전에 풀었던 10810, 10811 번과 유사한 바구니 문제이다. 이번엔 입력받은 i, j 의 값을 바꾸는 문제이다.

 

문제 분석

 

바구니를 N 개 가지고 있고, 각각의 바구니에는 1번부터  N 번까지 번호가 매겨져 있다. 이전에는 여기까지만 보고

 

바구니를 표현할 크기 N의 배열을 만들고, 바구니의 번호로 초기화 해야 겠다!

 

라고 생각했을 수 있지만, 이 문제는 바구니에 들어있는 공의 번호로 배열 요소를 초기화 해야 한다. 바구니에 들어있는 공을 교환하여 결과를 확인하고 싶은 것이므로 "공 = 배열 요소" 가 되어 배열 요소를 교환해야 한다.

 

바구니 번호는 Index 를 나타내기 위한 장치일 뿐이다. 입력으로 들어오는 i, j 의 범위를 간접적으로 나타내는 것으로 추후 배열의 Index 로 사용할때 이 범위를 생각하며 사용해야 한다.

 

M 번의 기회가 주어진다. 즉 M 번 반복한다는 뜻이고 M번의 i, j  에 대한 입력이 있을 것이다.

 

i 와 j 가 주어질 경우 바구니를 표현할 배열을 baskets[] 라고 할때, basket[i] 와 basket[j] 의 순서를 바꿔준다. 이 작업을 매 반복마다 입력되는 i, j 에 대해 수행해주면 된다.

 

정리하면

1. 바구니를 표현할 배열을 만들고 바구니에 들어있는 공의 번호로 배열 요소를 초기화 한다.

2. 입력받은 M 만큼 반복한다.

3. 반복 중 입력받은 i, j 의 배열 요소를 교환한다. 이때 Index 에 주의한다.

 

정답 코드
import java.io.BufferedReader
import java.io.InputStreamReader

fun main() {
    BufferedReader(InputStreamReader(System.`in`)).use { br ->
        val (basketCount, tryCount) = br.readLine().split(" ").map { it.toInt() }
        val basketArray = Array(basketCount) { it + 1 }

        repeat(tryCount) {
            val (idx1, idx2) = br.readLine().split(" ").map { it.toInt() - 1 }
            basketArray[idx1] = basketArray[idx2].also {
                basketArray[idx2] = basketArray[idx1]
            }
        }

        println(basketArray.joinToString(" "))
    }
}

 

Detail

i, j 의 범위

i, j 는 1보다 크거나 같은 값이 주어지므로 index 에 사용하기 위해 -1 을 하여 사용했다.

also 범위 지정 함수

Kotlin 에는 apply, let, run, with, also 5가지의 범위 지정 함수를 제공한다. 이에 대한 설명은 아래 링크를 참고하길 바란다.

 

[Kotlin] apply, run, with, let, also - 범위 지정 함수

코틀린의 범위 지정 함수- 특정 객체애 대한 작업을 블록(특정 객체에 대해 할 작업의 범위) 안에 넣어 실행할 수 있도록 하는 함수.- 구성 요소 : 수신 객체, 수신 객체 람다 [ 공식문서 ] https://kot

gangglion.tistory.com

 

 

Scope functions | Kotlin

 

kotlinlang.org

 

이 중, 문제에서 also 를 사용하였는데 이것이 가능한 이유는 also 는 수신객체 T 자신을 리턴하기 때문 이다.

 

also 의 형태를 보면

public inline fun <T> T.also(block: (T) -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block(this)
    return this
}

 

이런 식으로 수신객체 T 에 대해 T 가 람다식 내부로 전달되고, also 함수 자체는 T 를 반환한다.

 

문제에서 나온 교체 알고리즘은 흔히 이런 방식을 자주 사용하였다.

// array[1] 과 array[3] 을 교환한다고 가정
val array = arrayOf(0, 1, 2, 3, 4)
val temp = array[3]
array[3] = array[1]
array[1] = temp

 

하지만 also 를 쓰면 문제의 형태처럼

// array[1] 과 array[3] 을 교환한다고 가정
val array = arrayOf(0, 1, 2, 3, 4)
array[3] = array[1].also {
    array[1] = array[3]
}

 

이 가능하다. 이것이 가능한 이유를 순서대로 확인해보자.

 

  1. array[1].also { ... } 에서 범위지정함수 also 는 수신객체 자신을 리턴한다. 이부분에서 리턴되는 값은 1 이다.
    다음으론 also scope 가 수행되기 때문에
     array[3] 에 대입되지 않는다.
  2. also scope 로 수신객체 T 가 전달된다. 실제 전달된 T 는 array[1] 의 int 값이지만, scope 내에서 사용하지 않는다.
  3. scope 내의 동작 array[1] = array[3] 을 수행한다. array[1] 에는 3이 들어간다. array = [0, 3, 2, 3, 4] 의 형태가 된다.
  4. array[3] = array[1].also { ... } 를 수행한다. 여기서 array[1] 은 3번에서 변경된 값 3이 아닌, 1번에서 also 함수가 리턴한
    자기 자신, 즉 1이 된다. array[3] 에 1을 넣는다. array = [0, 3, 2, 1, 4] 의 형태가 된다.
  5. 결과적으로 array[1] 과 array[3] 의 순서가 변경된 결과가 나타난다.

kotlin 에서 also 의 특징을 사용하여 편하게 교체를 수행할 수 있다.

반응형