10.5.  Демонстрация - Автоответчик

Шаг 1

Подзадачи будут похожи на те, что были в прошлой задаче (это ведь та же самая задача, только решённая не за все дни, а только за дни от первого до каждого дня по очереди). Что будет результатом подзадачи? В каждый день у нас есть два выбора: продавать акцию или держать её. Если мы её продаём, то получаем прибыль как разницу между текущей ценой и встретившейся до текущего дня минимальной ценой. Если мы держим, прибыль будет такой же, как если бы мы продали акцию в прошлый день. Это следует из того, что мы либо уже получили максимальную прибыль с предыдущей продажи, и эта прибыль является максимальной до текущего дня, либо ещё не достигли самого пика и продавать её бессмысленно, так что имеет смысл снова принять это максимальной прибылью. Большее из этих двух чисел и есть решение подзадачи (обновление максимума почти как в прошлой задаче). Давайте теперь напишем наше решение.

import {answer} from './logger';

function countRatio(prices, profit) {
  answer(profit, 10, profit / 10)
}

countRatio([4,3,1,4,3,6], 3);

Шаг 2

Как видим из решения, некоторые части заметно перекликаются с предыдущей демонстрацией. Это стандартная ситуация для всех решений при помощи динамического программирования. Сначала мы заводим память для хранения результатов подзадач, а затем решаем их последовательно, в каждой используя решения предыдущих, от которых она зависит..


import {answer} from './logger';

function countRatio(prices, profit) {
  const profitByDays = Array(prices.length).fill(0);

  // Будем хранить отдельно минимальную цену за все прошедшие дни
  let minPrice = prices[0];

  for (let i = 1; i < prices.length; i++) {
    // Обновляем минимальную цену
    minPrice = Math.min(minPrice, prices[i]);

    // Если продаём, то получаем прибыль, равную разнице текущей и минимальной цен
    const sellProfit = prices[i] - minPrice;

    // Если не продаём, значит остаёмся при максимальной прибыли за предыдущие дни
    const holdProfit = prices[i - 1];

    // А максимальная прибыль получается при принятии лучшего решения
    profitByDays[i] = Math.max(sellProfit, holdProfit);
  }

  const maxProfit = profitByDays[profitByDays.length - 1];

  answer(profit, maxProfit, profit / maxProfit)
}

countRatio([4,3,1,4,3,6], 4);

File logger.js


const result = document.getElementById('result');

export function answer(current, perfect, ratio) {
  result.innerText = `Ты заработал ${current}$, а мог бы ${perfect}$. Ты на ${Math.round(ratio * 100)}% близок к идеалу!`;
}