ОП здесь: ответ @ MBo заставил меня задуматься (за это проголосовал за него), но, к сожалению, он не объяснил это так, как мне показалось достаточно ясным, поэтому мы идем.
ПозвольтеN
будет числом скобок.
НАИВНЫЙ ПОДХОД: Линейный поиск соответствующей налоговой скобки, вычисление налога на избыточный доход в скобке, а затем рекурсивное вычисление налогов наналогооблагаемый доход ниже скобки.Например, доход 15000
попадает в скобку, которая начинается с 10001
;налоги на эту скобку составляют (15000 - 10000) * 0.2 = 1000
+ налоги на 10000
.Это работает, но линейный поиск может занять O(N)
в худшем случае, и если начальный доход падает в наивысшей скобке, код будет повторяться N раз.В итоге мы получаем алгоритм O (N ^ 2).
ЛУЧШИЙ ПОДХОД: Двоичный поиск скобки, а затем продолжаем, как в наивном подходе.O(N log(N))
.
ДИНАМИЧЕСКИЙ ПОДХОД К ПРОГРАММИРОВАНИЮ: В этой задаче присутствуют оба критерия применения динамического программирования, оптимальной подструктуры и перекрывающихся подзадач.Зачем?Суммарные налоги для каждой скобки представляют собой сумму налогов для текущей скобки и налогов для оставшегося налогооблагаемого дохода.Для каждой скобки рекурсивное решение вычисляет налоги для нижних скобок снова и снова.
Таким образом, мы предварительно вычисляем и запоминаем налоги для налогооблагаемой прибыли до предыдущего сегмента снизу вверх.Это займет O(N)
времени.Двоичный поиск для скобки занимает log(N)
времени.Вычисление налогов теперь занимает O(1)
раз, что дает нам общий линейный алгоритм времени.
Scala code: Не стесняйтесь адаптироваться к вашему любимому языку программирования.
def taxes(income: Int, brackets: IndexedSeq[(Int, Double)]): Double = {
val dp = brackets
.zipWithIndex
.foldLeft((0d, IndexedSeq.empty[(Int, Double, Double)])) { case ((sum, acc), (cur, i)) =>
val taxesOnPrevBracket = if (i > 0) {
val prev = brackets(i - 1)
(cur._1 - prev._1) * prev._2
} else 0d
val cumulativeTaxes = sum + taxesOnPrevBracket
(cumulativeTaxes, acc :+ (cur._1, cur._2, cumulativeTaxes))
}
._2
@tailrec
def findBracket(start: Int, end: Int): Int = {
if (end - start <= 1) start
else {
val mid = start + (end - start) / 2
if (income > brackets(mid)._1) findBracket(mid, end)
else findBracket(start, mid)
}
}
val br = dp(findBracket(0, brackets.size - 1))
val inc = income - br._1 + 1
val tx = inc * br._2 + br._3
println(s"Taxable income: $income, bracket: $br, taxes: $tx")
tx
}
brackets
вот последовательность кортежей, начальных значений (спасибо @Dillon Davis за идею) и налоговой ставки.