/** * So lets try something else again. This is the closest pair problem in a 3d grid, described as a rare problem in CP4. * I would have like to implement it with a randomized algorithm, but using divide and conquer is faster to implement. */ import kotlin.math.pow import kotlin.math.sqrt fun main() { val reader = System.`in`.bufferedReader() val n = reader.readLine().toInt() val input = reader.lineSequence().take(n).map { string -> string.split(" ", limit = 3) .map { it.toDouble() } .let { (x, y, z) -> Coordinate(x, y, z) } }.sortedWith { o1, o2 -> o1.compareTo(o2) }.toList() println(input.conquer()) } typealias Coordinate = Triple fun List.divide() = take(size / 2) to drop(size / 2) fun List.conquer(): Double = when (size) { 1 -> Double.MAX_VALUE 2 -> first().distance(last()) else -> { val (left, right) = divide() //min distance is either left or right val d = left.conquer().coerceAtMost(right.conquer()) // or a combination of left and right with a max distance of d from the middle // (for 2d this is max 6 points, I guess for 3d a similar low number holds, so this should be O(log n)) val division = left.last() + (right.first() - left.last()) / 2.0 val leftCutOff = division - d val rightCutOff = division + d val overlap = left.takeLastWhile { it >= leftCutOff } + right.takeWhile { it <= rightCutOff } when(overlap.size) { size -> bruteforce().coerceAtMost(d) // nasty corner case all points are in overlap set, covered in example 1 0, 1 -> d else -> overlap.conquer().coerceAtMost(d) } } } fun List.bruteforce(): Double { var min = Double.MAX_VALUE for(i in indices) { for (j in i+1 until size) { min = get(i).distance(get(j)).coerceAtMost(min) } } return min } private operator fun Coordinate.compareTo(o2: Coordinate) = when (first) { o2.first -> when (second) { o2.second -> third.compareTo(o2.third) else -> second.compareTo(o2.second) } else -> first.compareTo(o2.first) } operator fun Coordinate.minus(other: Coordinate) = Triple(first - other.first, second - other.second, third - other.third) operator fun Coordinate.plus(other: Coordinate) = Triple(first + other.first, second + other.second, third + other.third) operator fun Coordinate.plus(d: Double) = Triple(first + d, second + d, third + d) operator fun Coordinate.minus(d: Double) = Triple(first - d, second - d, third - d) operator fun Coordinate.div(d: Double) = Triple(first / d, second / d, third / d) inline fun Coordinate.distance(other: Coordinate) = sqrt((first - other.first).pow(2) + (second - other.second).pow(2) + (third - other.third).pow(2)) fun generateRandom() { println(100000L) var i =0L while(i < 100000L) { println("${(1000000000L*Math.random()).toLong()} ${(1000000000L*Math.random()).toLong()} ${(1000000000L*Math.random()).toLong()}") i++ } }