https://coderun.yandex.ru/problem/cafe/description Средняя

Решение

import java.io.BufferedReader
import java.io.BufferedWriter
import java.io.InputStreamReader
import java.io.OutputStreamWriter
import kotlin.math.min

fun main() {
    val reader = BufferedReader(InputStreamReader(System.`in`))
    val writer = BufferedWriter(OutputStreamWriter(System.out))

    // Считываем N
    val N = reader.readLine().toInt()
    // Считываем стоимости обедов по дням в массив costs (1-based проще для понимания, но можно и 0-based)
    val costs = IntArray(N + 1)
    for (i in 1..N) {
        costs[i] = reader.readLine().toInt()
    }

    // Если N == 0, то ответ тривиален: суммарная стоимость = 0, купоны 0 и 0
    if (N == 0) {
        writer.write("0\\n")
        writer.write("0 0\\n")
        writer.close()
        return
    }

    // dp[i][k] = минимальная стоимость за первые i дней, если после i-го дня осталось k купонов
    // Инициализируем "бесконечностью" (здесь возьмём достаточно большое число)
    val INF = 10_000_000
    val dp = Array(N + 1) { IntArray(N + 1) { INF } }
    
    // Для восстановления ответа
    // prev[i][k] будет хранить пару (k_old, usedCoupon),
    // где usedCoupon = false/true (false = платили день i, true = использовали купон в день i)
    val prev = Array(N + 1) { Array<Pair<Int, Boolean>?>(N + 1) { null } }

    dp[0][0] = 0 // в начале 0 дней, 0 купонов, 0 рублей

    // Заполняем DP
    for (day in 1..N) {
        for (kOld in 0..N) {
            val oldCost = dp[day - 1][kOld]
            if (oldCost == INF) continue  // Невозможное состояние, пропускаем
            
            // 1) ПЛАТИМ за обед
            val costPay = oldCost + costs[day]
            val kNewPay = if (costs[day] > 100) kOld + 1 else kOld

            if (kNewPay <= N && costPay < dp[day][kNewPay]) {
                dp[day][kNewPay] = costPay
                prev[day][kNewPay] = Pair(kOld, false)
            }

            // 2) ИСПОЛЬЗУЕМ купон (если есть)
            if (kOld > 0) {
                val costUseCoupon = oldCost // бесплатно
                val kNewCoupon = kOld - 1
                if (costUseCoupon < dp[day][kNewCoupon]) {
                    dp[day][kNewCoupon] = costUseCoupon
                    prev[day][kNewCoupon] = Pair(kOld, true)
                }
            }
        }
    }

    // Ищем минимальную стоимость среди dp[N][k] и при равенстве - максимальный k
    var minCost = INF
    var bestK = 0
    for (k in 0..N) {
        if (dp[N][k] < minCost) {
            minCost = dp[N][k]
            bestK = k
        } else if (dp[N][k] == minCost && k > bestK) {
            // При одинаковой стоимости выбираем большее k
            bestK = k
        }
    }

    // Восстановим путь (дни использования купонов)
    val usedDays = mutableListOf<Int>()
    var curK = bestK
    var i = N

    while (i > 0) {
        val (prevK, usedCoupon) = prev[i][curK]!!
        if (usedCoupon) {
            // Значит в день i использовали купон
            usedDays.add(i)
        }
        // Переходим к предыдущему состоянию
        curK = prevK
        i--
    }

    usedDays.reverse() // чтобы вывести в порядке возрастания дня

    // Количество использованных купонов:
    val K2 = usedDays.size
    // Количество оставшихся купонов:
    val K1 = bestK

    // Вывод результатов:
    writer.write("$minCost\\n")
    writer.write("$K1 $K2\\n")
    for (day in usedDays) {
        writer.write("$day\\n")
    }

    writer.close()
}