Skip to content
dsa450for five year olds

Greedy · 9 of 35

Buy Maximum Stocks if i stocks can be bought on i-th day

Buy as many shares as your money allows when day i lets you buy at most i shares, by shopping the cheapest days first.

Simple way
time O((n+1)!)
room O(n)
Clever way
time O(n log n)
room O(n)

The problem, simply

A stock has a price on each of n days: price[i] on day i + 1. The rule: on day 1 you may buy at most 1 share, on day 2 at most 2 shares, and on day i at most i shares. You start with k money and never get more.

Give back the largest number of shares you can buy. Which days you use does not matter, only how many shares you end up with.

A story to remember it

Picture it

arrayprice
0123459374
arraylimit
0123412345

Five days. Day 1 costs 5 and allows 1 share, day 2 costs 9 and allows 2, and so on. The budget is 60.

budget
60

Examples

A normal case

In
price = [5, 9, 3, 7, 4], k = 60
Out
12

3 shares at 3, 5 at 4, 1 at 5, and 3 of the 4 allowed at 7. That spends 55.

Three days

In
price = [10, 7, 19], k = 45
Out
4

2 shares at 7 (14), 1 at 10 (10), then 1 at 19 (19). 43 spent, 2 left.

Money for everything

In
price = [7, 10, 4], k = 100
Out
6

All 1 + 2 + 3 = 6 shares cost 7 + 20 + 12 = 39. No more are allowed.

Too poor

In
price = [5, 6], k = 4
Out
0

Even the cheapest share costs more than you have.

Way 1: the simple way

Try every choice. For each day, try buying 0, 1, up to the limit, as long as the money lasts. Keep the biggest total.

  1. Start at day 1 with the full budget.
  2. For each amount you could buy today, pay for it and move on to the next day.
  3. When the days run out, compare the number of shares with the best so far.
java
class Solution {
    private long best = 0;

    public long buyMaximumProducts(int[] price, long k) {
        best = 0;
        tryDays(price, 0, k, 0);
        return best;
    }

    private void tryDays(int[] price, int day, long money, long shares) {
        best = Math.max(best, shares);
        if (day == price.length) return;
        int limit = day + 1;                              // day i allows i shares
        for (int buy = 0; buy <= limit; buy++) {
            long cost = (long) buy * price[day];
            if (cost > money) break;                      // cannot afford more today
            tryDays(price, day + 1, money - cost, shares + buy);
        }
    }
}

How long it takes: day i offers i + 1 choices, so there can be 2 * 3 * ... * (n + 1) paths, which is O((n+1)!). Extra room: the recursion, O(n).

Way 2: the clever way

A share is a share. To get the most of them, buy the cheapest ones first.

  1. Pair each price with its limit: day i (counting from 1) gives the pair (price, i).
  2. Sort the pairs by price, cheapest first.
  3. For each pair, buy min(limit, money / price) shares and pay for them.
  4. Add up the shares.

Why is this safe? If your plan buys a pricier share while a cheaper offer still has room, swap them. You get the same number of shares and spend less, so you can only gain.

Cheapest offers first, as many as allowed

01 / 07

arrayprice, cheapest first
0123434579
arraylimit
01234day 3: 3day 5: 5day 1: 1day 4: 4day 2: 2

Pair each price with its day limit (day i allows i shares), then sort by price. Budget: 60.

budget
60
shares
0
  • looking now
  • checking
  • done
  • answer
  • rejected
  • ignored
java
class Solution {
    public long buyMaximumProducts(int[] price, long k) {
        int n = price.length;
        int[][] offers = new int[n][2];
        for (int i = 0; i < n; i++) {
            offers[i][0] = price[i];
            offers[i][1] = i + 1;                 // day i+1 allows i+1 shares
        }
        Arrays.sort(offers, (a, b) -> Integer.compare(a[0], b[0])); // cheapest first

        long shares = 0, money = k;
        for (int[] o : offers) {
            long buy = Math.min(o[1], money / o[0]);   // the limit, or what money allows
            shares += buy;
            money -= buy * o[0];
        }
        return shares;
    }
}

How long it takes: the sort is O(n log n), the walk is O(n). Extra room: the pairs, O(n).

Watch out for

  • Day 1 allows 1 share, not 0. With 0 based indexes, the limit is i + 1.
  • buy * price can pass the int range. Use long.
  • Do not stop at the first offer you cannot fully afford. Buy the part you can afford, then the later, pricier offers buy nothing anyway.

What interviewers ask next

  • What if each day allowed any number of shares? Then only the cheapest price matters: answer k / min(price).
  • What if you must buy on days in order and can sell too? That is a different family: the stock buy and sell problems with dynamic programming.
  • Can you skip the sort? With prices in a small range, bucket them by price and walk the buckets, O(n + maxPrice).
  • Why is greedy optimal? Exchange argument: swapping a pricier share for a cheaper one with spare room never lowers the count and never costs more.

Remember this

Grown-up words: the offers are (price, quantity) pairs, buying the cheapest first is a greedy choice, and the swap reasoning is an exchange argument.