AtCoder B,C問題をKotlinで解こう - ABC255

当記事ではAtCoder、ABCのB問題ならびにC問題（時々D問題も）のKotlinでの解法を超初心者向けに詳細に解説します。

https://atcoder.jp/contests/abc255/tasks

## [B - Light It Up](https://atcoder.jp/contests/abc255/tasks/abc255_b)
#### やりたいこと
人（0~N-1、配列に入れることを考慮して0始まりとする）のそれぞれについて、**最も近くにいる明かりを持った人との距離** を算出しよう。なお、本人が明かりを持っている場合は0とする。
#### 位置情報を保持するクラスの作成
人の位置を保持し、最も近くにいる明かりを持った人間の検出をスムーズにするため、位置情報クラスを作成しよう。
```kotlin
// 位置情報を保持するクラス
class Loc(private val x: Int, private val y: Int) {
    // 他の位置情報との距離を求めるメソッド
    fun diff(other: Loc): Double {
        return ((x - other.x).let { it.toLong() * it } + (y - other.y).let { it.toLong() * it }).let { Math.sqrt(it.toDouble()) }
    }
}
```
#### 入力値の取得
```kotlin
    // 入力値の取得
    val (n, k) = readLine()!!.split(" ").map { it.toInt() }
    val a = readLine()!!.split(" ").map { it.toInt() - 1 }
    val xy = (1..n).map { readLine()!!.split(" ").map { it.toInt() }.let { Loc(it[0], it[1]) } }
```
#### 明かりを持った人間との距離
人(0~n-1)について、 **最も近くにいる明かりを持った人間** との距離を取得しよう。
```kotlin
    // 人（0~n-1)のそれぞれ最も近い「明かりを持った人」との距離を求める。
    val farToLights = xy.map { target -> a.map { xy[it].diff(target) }.min()!! }
```
#### サンプルコード
```kotlin:main.kt
fun main(args: Array<String>) {
    // 入力値の取得
    val (n, k) = readLine()!!.split(" ").map { it.toInt() }
    val a = readLine()!!.split(" ").map { it.toInt() - 1 }
    val xy = (1..n).map { readLine()!!.split(" ").map { it.toInt() }.let { Loc(it[0], it[1]) } }

    // 人（0~n-1)のそれぞれ最も近い「明かりを持った人」との距離を求める。
    val farToLights = xy.map { target -> a.map { xy[it].diff(target) }.min()!! }

    // 上記の最大値が答えとなる
    println(farToLights.max())

}

// 位置情報を保持するクラス
class Loc(private val x: Int, private val y: Int) {
    // 他の位置情報との距離を求めるメソッド
    fun diff(other: Loc): Double {
        return ((x - other.x).let { it.toLong() * it } + (y - other.y).let { it.toLong() * it }).let { Math.sqrt(it.toDouble()) }
    }
}
```
## [C - ±1 Operation 1](https://atcoder.jp/contests/abc255/tasks/abc255_c)
#### やりたいこと
等差数列Sに含まれる数値のうち、Xに最も近いものとXの差（の絶対値）を求めよう。
#### 入力値の取得
```kotlin
    val (x, a, d, n) = readLine()!!.split(" ").map { it.toLong() }
```
#### 等差数列Sの最大値と最小値を決めよう
等差数列Sの初項はa、公差はd、要素数はnとなる。そこからSの最終項は $a + d(n - 1)$ となることが分かる。
等差数列Sの最大値と最小値を決定しよう。公差はマイナスになることがあるので、その場合は初項と最終項を入れ替えて、公差の絶対値を後の処理として使用しよう。

```kotlin
    // 等差数列Sの最終項の取得
    val last = a + (d * (n - 1))

    // 等差数列Sの最小値の取得
    val fixedFirst = if (d >= 0) a else last
    // 等差数列Sの最大値の取得
    val fixedLast = if (d >= 0) last else a

    // 最適化された等差数列の公差
    val fixedD = Math.abs(d)

```
#### 場合分けをして処理しよう
##### 等差数列の数値が１種類のみの場合（要素数が1または交差が0）
```kotlin
    if (fixedFirst == fixedLast) {
        // 等差数列Sの要素が一つだけの場合
        return Math.abs(x - fixedFirst)
    }
```
##### xが等差数列Sの範囲内に含まれない場合
Sの最大値と最小値、それぞれの差（の絶対値）の内、小さい方が答えとなる。
```kotlin
    if (fixedFirst == fixedLast) {
        // 等差数列Sの要素が一つだけの場合
        return Math.abs(x - fixedFirst)
    } else if (x !in fixedFirst..fixedLast) {
        // x が等差数列の最小〜最大の範囲に含まれない場合
        return Math.min(Math.abs(fixedFirst - x), Math.abs(fixedLast - x))
    }
```
##### xが等差数列Sの範囲内に含まれる場合
Sに含まれる要素の内、x以下の最大値とx以降の最小値を求め、それらとの差の絶対値を求めよう。
```kotlin
    if (fixedFirst == fixedLast) {
        // 等差数列Sの要素が一つだけの場合
        return Math.abs(x - fixedFirst)
    } else if (x !in fixedFirst..fixedLast) {
        // x が等差数列の最小〜最大の範囲に含まれない場合
        return Math.min(Math.abs(fixedFirst - x), Math.abs(fixedLast - x))
    } else {
        // 等差数列Sに含まれる数値から、X以下で最大の数値を取得する
        val near1 = ((x - fixedFirst) / fixedD) * fixedD + fixedFirst

        // 等差数列Sに含まれる数値から、X以上で最小の数値を取得する
        val near2 = fixedLast - ((fixedLast - x) / fixedD) * fixedD

        return Math.min(Math.abs(x - near1), Math.abs(near2 - x))
    }
```
#### サンプルコード
```kotlin:main.kt
fun main(args: Array<String>) {
    println(getAns())
}

fun getAns(): Long {
    val (x, a, d, n) = readLine()!!.split(" ").map { it.toLong() }

    // 等差数列Sの最終項の取得
    val last = a + (d * (n - 1))

    // 等差数列Sの最小値の取得
    val fixedFirst = if (d >= 0) a else last
    // 等差数列Sの最大値の取得
    val fixedLast = if (d >= 0) last else a

    // 最適化された等差数列の公差
    val fixedD = Math.abs(d)

    if (fixedFirst == fixedLast) {
        // 等差数列Sの要素が一つだけの場合
        return Math.abs(x - fixedFirst)
    } else if (x !in fixedFirst..fixedLast) {
        // x が等差数列の最小〜最大の範囲に含まれない場合
        return Math.min(Math.abs(fixedFirst - x), Math.abs(fixedLast - x))
    } else {
        // 等差数列Sに含まれる数値から、X以下で最大の数値を取得する
        val near1 = ((x - fixedFirst) / fixedD) * fixedD + fixedFirst

        // 等差数列Sに含まれる数値から、X以上で最小の数値を取得する
        val near2 = fixedLast - ((fixedLast - x) / fixedD) * fixedD

        return Math.min(Math.abs(x - near1), Math.abs(near2 - x))
    }
}
```