DEV Community

faangmaster
faangmaster

Posted on • Edited on

Простая задача на динамическое программирование: Лучшее время для покупки и продажи акции

Задача.

Дан массив целых чисел prices, где prices[i] - цена на акцию в день i. Вам нужно максимизировать прибыль выбрав один день, когда вы купите одну акцию, а также другой день в будущем, когда вы продадите акцию. В качестве результата нужно вернуть максимальную прибыль, которую можно получить. Если прибыль получить нельзя, то вернуть 0.

Ссылка на leetcode: https://leetcode.com/problems/best-time-to-buy-and-sell-stock/description/

Пример:
prices = [7,1,5,3,6,4]
Ответ: 5 (Купить по 1, продать по 6).

Решение.

Brute force.

Первое решение, которое приходит в голову, это перебрать все пары, посчитать для каждой прибыль и найти максимальную прибыль. Такое решение работает за O(n^2) по времени и O(1) по памяти.
Перебор пар можно сделать при помощи вложенного цикла:

for (int i = 0; i < prices.length; i++) {
    for (int j = i + 1; j < prices.length; j++) {
        ......
    }
}
Enter fullscreen mode Exit fullscreen mode

Тогда прибыль для покупки в день i и продажи в день j:
int profit = prices[j] - prices[i];
И нам нужно найти максимум:

public int maxProfit(int[] prices) {
    int maxProfit = Integer.MIN_VALUE;
    for (int i = 0; i < prices.length; i++) {
        for (int j = i + 1; j < prices.length; j++) {
            maxProfit = Math.max(maxProfit, prices[j] - prices[i]);
        }
    }
    return maxProfit > 0 ? maxProfit : 0;
}
Enter fullscreen mode Exit fullscreen mode

Dynamic Programming.

Можно ли улучшить это решение? Да, используя динамическое программирование. Давайте применим bottom-up подход. Для этого нужно разбить задачу на подзадачи, а также установить связь решения подзадач с решением более общей задачи.
В данном случае, такой подзадачей может быть максимальная прибыль, которую мы можем получить, если решим продавать в день i. Тогда решением общей задачи будет максимум из всех таких вариантов (максимум из прибылей, когда мы продаем в день 1, день 2, день 3 и т.д.). Как нам определить максимальную прибыль, которую мы можем получить, если продавать в день i? Очевидно, такая прибыль будет максимальная, когда мы купили в день с минимальной ценой в какой-то день до текущего дня i.

maxProfit(i) = prices[i] - min(prices[j], j = 0, .., i - 1)
Enter fullscreen mode Exit fullscreen mode

Тогда ответ будет максимум из таких прибылей:

maxProfit = max(maxProfit(i), i = 1, .., n - 1)
Enter fullscreen mode Exit fullscreen mode

Для того, чтобы нам снова и снова не перевычислять min(prices[j], j = 0, .., i - 1), мы можем хранить это в переменной minPriceSoFar и обновлять ее по мере итерации по массиву.
Код решения:

public int maxProfit(int[] prices) {
    int maxProfit = Integer.MIN_VALUE;
    int minPriceSoFar = Integer.MAX_VALUE;
    for (int i = 0; i < prices.length; i++) {
        minPriceSoFar = Math.min(minPriceSoFar, prices[i]);
        int currentProfit = prices[i] - minPriceSoFar;
        maxProfit = Math.max(maxProfit, currentProfit);
    }
    return maxProfit;
}
Enter fullscreen mode Exit fullscreen mode

Решение работает за O(n) по времени и O(1) по памяти.

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

Top comments (0)

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay