136 lines
4.7 KiB
Kotlin
Executable File
136 lines
4.7 KiB
Kotlin
Executable File
#!/usr/bin/env kotlin
|
|
import java.util.Scanner
|
|
|
|
|
|
val scanner = Scanner(System.`in`)
|
|
|
|
class Map(private val grid: List<List<String>>) {
|
|
private val fields = mutableSetOf<Field>()
|
|
private val map = mutableListOf<List<Plot>>()
|
|
init {
|
|
for ((y, line) in grid.withIndex()) {
|
|
map.add(line.mapIndexed { x, s -> Plot(Pair(x, y), s) })
|
|
}
|
|
for (y in 0 until this.getHeight()) {
|
|
for (x in 0 until this.getWidth()) {
|
|
val plot = getPlot(x, y)
|
|
if (!plot.hasField()) {
|
|
val field = Field(this)
|
|
plot.addField(field)
|
|
fields.add(field)
|
|
field.expand(plot)
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
fun getWidth(): Int = grid[0].size
|
|
fun getHeight(): Int = grid.size
|
|
fun getPlot(x: Int, y: Int): Plot = map[y][x]
|
|
|
|
fun getFields(): Set<Field> = fields
|
|
|
|
fun getPrice(): Long = fields.sumOf { it.getPrice() }
|
|
fun getBulkPrice(): Long = fields.sumOf { it.getBulkPrice() }
|
|
|
|
}
|
|
|
|
class Field(private val map: Map) {
|
|
private val fieldPlots = mutableSetOf<Plot>()
|
|
private val borderPlots = mutableSetOf<Pair<Plot, Plot>>()
|
|
fun addPlot(plot: Plot) {
|
|
fieldPlots.add(plot)
|
|
}
|
|
|
|
fun expand(plot: Plot) {
|
|
val (x, y) = plot.getPosition()
|
|
val checkPositions = listOf(Pair(x - 1, y), Pair(x + 1, y), Pair(x, y - 1), Pair(x, y + 1))
|
|
for ((checkX, checkY) in checkPositions) {
|
|
if (checkX < 0 || checkX >= map.getWidth() || checkY < 0 || checkY >= map.getHeight()) {
|
|
addBorder(plot, Plot(Pair(checkX, checkY), "."))
|
|
continue
|
|
}
|
|
val checkedPlot = map.getPlot(checkX, checkY)
|
|
if (plot.getType() == checkedPlot.getType()) {
|
|
if (!checkedPlot.hasField()) {
|
|
checkedPlot.addField(this)
|
|
expand(checkedPlot)
|
|
}
|
|
} else {
|
|
addBorder(plot, checkedPlot)
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
private fun addBorder(plot: Plot, checkedPlot: Plot) {
|
|
borderPlots.add(Pair(plot, checkedPlot))
|
|
}
|
|
|
|
fun getPrice(): Long {
|
|
return getArea() * getCircumference()
|
|
}
|
|
|
|
fun getArea(): Long = fieldPlots.size.toLong()
|
|
fun getCircumference(): Long = borderPlots.size.toLong()
|
|
fun getBulkPrice(): Long {
|
|
return getArea() * getNumberOfSides()
|
|
}
|
|
|
|
private fun getNumberOfSides(): Long {
|
|
val leftBorders = borderPlots.filter { it.first.getPosition().first > it.second.getPosition().first}.map { it.first.getPosition() }
|
|
val rightBorders = borderPlots.filter { it.first.getPosition().first < it.second.getPosition().first}.map { it.first.getPosition() }
|
|
val topBorders = borderPlots.filter { it.first.getPosition().second > it.second.getPosition().second}.map { it.first.getPosition() }
|
|
val bottomBorders = borderPlots.filter { it.first.getPosition().second < it.second.getPosition().second}.map { it.first.getPosition() }
|
|
|
|
return countSides(leftBorders) + countSides(rightBorders) + countSides(topBorders) + countSides(bottomBorders)
|
|
}
|
|
|
|
private fun countSides(coordinates: List<Pair<Int, Int>>): Long {
|
|
val visited = mutableSetOf<Pair<Int, Int>>()
|
|
val coordinateSet = coordinates.toSet()
|
|
var sideCount = 0
|
|
val directions = listOf(Pair(1, 0), Pair(0, 1), Pair(-1, 0), Pair(0, -1))
|
|
|
|
fun dfs(coord: Pair<Int, Int>) {
|
|
val stack = mutableListOf(coord)
|
|
while (stack.isNotEmpty()) {
|
|
val current = stack.removeLast()
|
|
if (current in visited) continue
|
|
visited.add(current)
|
|
directions.map { (dx, dy) -> Pair(current.first + dx, current.second + dy) }
|
|
.filter { it in coordinateSet && it !in visited }
|
|
.forEach { stack.add(it) }
|
|
}
|
|
}
|
|
for (coord in coordinates) {
|
|
if (coord !in visited) {
|
|
sideCount++
|
|
dfs(coord)
|
|
}
|
|
}
|
|
|
|
return sideCount.toLong()
|
|
}
|
|
}
|
|
|
|
class Plot(private val position: Pair<Int, Int>, private val type: String) {
|
|
private var field: Field? = null
|
|
fun addField(field: Field) {
|
|
this.field = field
|
|
field.addPlot(this)
|
|
}
|
|
|
|
fun hasField(): Boolean = field != null
|
|
fun getPosition(): Pair<Int, Int> = position
|
|
fun getType(): String = type
|
|
}
|
|
|
|
val grid = mutableListOf<List<String>>()
|
|
while (scanner.hasNextLine()) {
|
|
grid.add(scanner.nextLine().split("").filter { it.isNotEmpty() })
|
|
}
|
|
|
|
val map = Map(grid.reversed())
|
|
println("The price is: ${map.getPrice()}")
|
|
println("The bulk price is ${map.getBulkPrice()}") |