https://coderun.yandex.ru/problem/plane-boarding/description Средняя

Решение

import java.io.BufferedReader
import java.io.BufferedWriter
import java.io.InputStreamReader
import java.io.OutputStreamWriter

data class Person(
    var a: Int,      // Время на размещение ручной клади (с учётом добавочного времени, если нужно пропустить)
    var row: Int,    // Номер ряда (смещённый на 180, чтобы удобнее считать)
    var pos: Int,    // Текущая позиция пассажира в проходе (также "магия" со сдвигом, чтобы упорядочить)
    var seated: Boolean = false // Флаг, что пассажир уже сел
)

fun main() {
    val reader = BufferedReader(InputStreamReader(System.`in`))
    val writer = BufferedWriter(OutputStreamWriter(System.out))
    
    val n = reader.readLine().toInt()
    val persons = ArrayList<Person>(n)
    
    // Для учёта занятых мест в каждом ряду (нужно, чтобы понять, сколько человек нужно пропустить).
    // Ключ: номер ряда (1..30). Значение: множество занятых мест в этом ряду (A..F).
    val occupiedSeatsInRow = mutableMapOf<Int, MutableSet<Char>>()
    
    // Функция для получения количества людей, которых придётся "попросить выйти"
    // с учётом уже занятых мест. Возвращает 0, 1 или 2.
    fun countNeighborsToStandUp(row: Int, seat: Char): Int {
        val occupied = occupiedSeatsInRow.getOrPut(row) { mutableSetOf() }
        return when (seat) {
            'A' -> {
                var cnt = 0
                if ('B' in occupied) cnt++
                if ('C' in occupied) cnt++
                cnt
            }
            'B' -> if ('C' in occupied) 1 else 0
            'C' -> 0
            'D' -> 0
            'E' -> if ('D' in occupied) 1 else 0
            'F' -> {
                var cnt = 0
                if ('D' in occupied) cnt++
                if ('E' in occupied) cnt++
                cnt
            }
            else -> 0
        }
    }

    repeat(n) { i ->
        val line = reader.readLine().trim().split(" ")
        val aInput = line[0].toInt()
        val seatString = line[1]
        
        // seatString имеет формат вида "12A", "1B", "30F" и т.д.
        // Нужно отделить номер ряда (числовую часть) от буквы.
        val rowPart = seatString.substring(0, seatString.length - 1)
        val seatPart = seatString.last()  // одна из A..F
        
        val row = rowPart.toInt()
        
        // Считаем, сколько людей нужно пропустить, исходя из уже занятых мест
        val k = countNeighborsToStandUp(row, seatPart)
        
        // Добавим 5 или 15 к aInput
        val aTime = when (k) {
            1 -> aInput + 5
            2 -> aInput + 15
            else -> aInput
        }
        
        // Помечаем место в данном ряду как занятое
        occupiedSeatsInRow[row]!!.add(seatPart)
        
        // "Магические" сдвиги, как в C++ коде:
        //   - row + 180, чтобы у более дальних рядов получалось большее значение
        //   - pos = 180 - i, чтобы первый по порядку введённый в данных оказался в конце списка (последним в проходе)
        // Потом мы развернём список, чтобы идти от тех, кто первым зашёл на борт, к тем, кто зашёл последним.
        persons.add(Person(aTime, row + 180, 180 - i, false))
    }
    
    // Разворачиваем, чтобы первый пассажир (по входным данным) оказался в голове очереди.
    persons.reverse()
    
    var time = 0
    // Пока есть хоть один пассажир, который не сел.
    var currentList = persons

    while (currentList.isNotEmpty()) {
        time++
        val newList = ArrayList<Person>(currentList.size)
        val size = currentList.size
        
        // Идём с хвоста к голове, чтобы при необходимости "проталкивать" пассажиров вперёд
        // (аналог цикла for (i = n-1; i >= 0; --i) в C++).
        for (i in size - 1 downTo 0) {
            val p = currentList[i]
            // Если пассажир достиг своего ряда:
            if (p.pos == p.row) {
                // Если он уже "достал ручную кладь" (a == 0), то он садится (seated = true).
                if (p.a == 0) {
                    p.seated = true
                    // seated-пассажира не надо класть в newList: он больше не блокирует проход
                } else {
                    // Иначе уменьшаем время на 1, пока он укладывает багаж, и он остаётся в проходе
                    p.a--
                    newList.add(p)
                }
            } else {
                // Если пассажир ещё не дошёл до своего ряда, проверяем, может ли он сделать шаг вперёд:
                //   1) он — последний в списке (i == size-1), тогда перед ним нет никого
                //   2) либо следующего в списке (i+1) нет в том месте, куда мы хотим ступить
                //     (то есть currentList[i].pos+1 != currentList[i+1].pos) или
                //     он уже seated (тогда проход свободен для нас).
                val canMoveForward = when {
                    i == size - 1 -> true
                    else -> {
                        val nextP = currentList[i + 1]
                        (p.pos + 1 != nextP.pos) || nextP.seated
                    }
                }
                if (canMoveForward) {
                    p.pos++
                }
                newList.add(p)
            }
        }
        // Теперь перевернём newList, чтобы снова в начале списка был "первый" пассажир по логике прохода.
        newList.reverse()
        
        // Фильтруем всех, кто seated, – они уже выбывают из прохода, но обратите внимание:
        // Здесь удобно сначала собрать всех (кроме тех, кто не попал, потому что seated), 
        // а потом отсеять seated-пассажиров.
        val filtered = newList.filter { !it.seated }
        currentList = ArrayList(filtered)
    }
    
    // Вычитаем 1, так как в конце цикла мы ещё раз сделали time++
    writer.write("${time - 1}\\n")
    writer.flush()
    reader.close()
    writer.close()
}