Problem

It is a sweltering summer day, and a boy wants to buy some ice cream bars.

At the store, there are n ice cream bars. You are given an array costs of length n, where costs[i] is the price of the ith ice cream bar in coins. The boy initially has coins coins to spend, and he wants to buy as many ice cream bars as possible.

Note: The boy can buy the ice cream bars in any order.

Return _themaximum number of ice cream bars the boy can buy with _coins coins.

You must solve the problem by counting sort.

Examples

Example 1

1
2
3
Input: costs = [1,3,2,4,1], coins = 7
Output: 4
Explanation: The boy can buy ice cream bars at indices 0,1,2,4 for a total price of 1 + 3 + 2 + 1 = 7.

Example 2

1
2
3
Input: costs = [10,6,8,7,7,8], coins = 5
Output: 0
Explanation: The boy cannot afford any of the ice cream bars.

Example 3

1
2
3
Input: costs = [1,6,3,1,2,5], coins = 20
Output: 6
Explanation: The boy can buy all the ice cream bars for a total price of 1 + 6 + 3 + 1 + 2 + 5 = 18.

Constraints

  • costs.length == n
  • 1 <= n <= 10^5
  • 1 <= costs[i] <= 10^5
  • 1 <= coins <= 10^8

Solution

Method 1 – Counting Sort Greedy

Intuition

To maximize the number of ice cream bars, always buy the cheapest bars first. Since costs are bounded, we can use counting sort to efficiently count the number of bars at each price, then buy as many as possible from cheapest to most expensive until coins run out.

Approach

  1. Find the maximum cost in the array to determine the counting sort range.
  2. Create a count array where count[i] is the number of bars costing i coins.
  3. For each price from lowest to highest:
    • Buy as many bars as possible at that price, without exceeding the available coins.
    • Subtract the total cost from coins and add the number of bars bought to the answer.
    • Stop if coins run out.
  4. Return the total number of bars bought.

Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class Solution {
public:
    int maxIceCream(vector<int>& costs, int coins) {
        int mx = *max_element(costs.begin(), costs.end());
        vector<int> cnt(mx + 1);
        for (int c : costs) cnt[c]++;
        int ans = 0;
        for (int i = 1; i <= mx && coins > 0; ++i) {
            int buy = min(cnt[i], coins / i);
            ans += buy;
            coins -= buy * i;
        }
        return ans;
    }
};
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
func maxIceCream(costs []int, coins int) int {
    mx := 0
    for _, c := range costs {
        if c > mx {
            mx = c
        }
    }
    cnt := make([]int, mx+1)
    for _, c := range costs {
        cnt[c]++
    }
    ans := 0
    for i := 1; i <= mx && coins > 0; i++ {
        buy := cnt[i]
        if buy > coins/i {
            buy = coins / i
        }
        ans += buy
        coins -= buy * i
    }
    return ans
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class Solution {
    public int maxIceCream(int[] costs, int coins) {
        int mx = 0;
        for (int c : costs) mx = Math.max(mx, c);
        int[] cnt = new int[mx + 1];
        for (int c : costs) cnt[c]++;
        int ans = 0;
        for (int i = 1; i <= mx && coins > 0; ++i) {
            int buy = Math.min(cnt[i], coins / i);
            ans += buy;
            coins -= buy * i;
        }
        return ans;
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class Solution {
    fun maxIceCream(costs: IntArray, coins: Int): Int {
        var mx = 0
        for (c in costs) mx = maxOf(mx, c)
        val cnt = IntArray(mx + 1)
        for (c in costs) cnt[c]++
        var ans = 0
        var coinsLeft = coins
        for (i in 1..mx) {
            if (coinsLeft <= 0) break
            val buy = minOf(cnt[i], coinsLeft / i)
            ans += buy
            coinsLeft -= buy * i
        }
        return ans
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class Solution:
    def maxIceCream(self, costs: list[int], coins: int) -> int:
        mx = max(costs)
        cnt = [0] * (mx + 1)
        for c in costs:
            cnt[c] += 1
        ans = 0
        for i in range(1, mx + 1):
            if coins <= 0:
                break
            buy = min(cnt[i], coins // i)
            ans += buy
            coins -= buy * i
        return ans
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
impl Solution {
    pub fn max_ice_cream(costs: Vec<i32>, coins: i32) -> i32 {
        let mx = *costs.iter().max().unwrap();
        let mut cnt = vec![0; (mx + 1) as usize];
        for &c in &costs {
            cnt[c as usize] += 1;
        }
        let mut ans = 0;
        let mut coins = coins;
        for i in 1..=mx {
            let buy = cnt[i as usize].min(coins / i);
            ans += buy;
            coins -= buy * i;
            if coins <= 0 { break; }
        }
        ans
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class Solution {
    maxIceCream(costs: number[], coins: number): number {
        const mx = Math.max(...costs);
        const cnt = Array(mx + 1).fill(0);
        for (const c of costs) cnt[c]++;
        let ans = 0;
        for (let i = 1; i <= mx && coins > 0; ++i) {
            const buy = Math.min(cnt[i], Math.floor(coins / i));
            ans += buy;
            coins -= buy * i;
        }
        return ans;
    }
}

Complexity

  • ⏰ Time complexity: O(n + C), where n is the number of bars and C is the maximum cost. Counting sort and the greedy loop are both linear.
  • 🧺 Space complexity: O(C), for the counting array.