Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions problems/1833-maximum-ice-cream-bars/analysis.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# 1833. Maximum Ice Cream Bars

[LeetCode Link](https://leetcode.com/problems/maximum-ice-cream-bars/)

Difficulty: Medium
Topics: Array, Greedy, Sorting, Counting Sort
Acceptance Rate: 75.4%

## Hints

### Hint 1

You want to maximize the *count* of items you buy, not which specific ones. When the goal is "as many as possible" and items have no other constraints tying them together, ask yourself: does the order in which I pick them matter, and is there an obviously "best first" choice?

### Hint 2

This is a greedy problem. To fit the most bars into a fixed budget, always reach for the cheapest bar still available. If you sort the costs ascending and spend from the front, you can never do better by skipping a cheap bar in favor of a more expensive one. The only question left is *how* to sort efficiently.

### Hint 3

Notice the constraint: `1 <= costs[i] <= 10^5`. The values live in a small, bounded range, which is the classic signal for **counting sort**. Instead of comparison-sorting in O(n log n), tally how many bars exist at each price into a frequency array, then walk the prices from cheapest to most expensive, buying as many as your coins allow at each price level. This gives a linear-time greedy.

## Approach

The core idea is greedy: to buy the maximum number of bars, always buy the cheapest ones first. Spending a coin on an expensive bar when a cheaper one is available can only reduce the total count, so sorting ascending and buying from the cheapest end is optimal.

Because the problem explicitly asks for **counting sort**, we avoid a comparison sort and instead exploit the bounded value range (`costs[i] <= 10^5`):

1. **Build a frequency table.** Create a `count` array indexed by price. For each cost `c`, increment `count[c]`. This buckets every bar by its price in O(n).

2. **Walk prices from cheapest to most expensive.** For each price `p` from 1 up to the maximum cost:
- If `count[p]` is 0, skip it.
- Otherwise, figure out how many bars at this price we can afford: `affordable = coins / p`, capped at the number actually available `count[p]`.
- Add that many bars to the answer and subtract their total cost (`bought * p`) from `coins`.
- If after this we cannot afford even one more bar at the *current* price, then we certainly cannot afford anything more expensive either — so we can stop early.

3. **Return the running count** of bars bought.

**Walkthrough on Example 1:** `costs = [1,3,2,4,1]`, `coins = 7`.
- Frequency: price 1 → 2 bars, price 2 → 1, price 3 → 1, price 4 → 1.
- Price 1: can afford `7/1 = 7`, but only 2 exist → buy 2, coins = 5, total = 2.
- Price 2: can afford `5/2 = 2`, only 1 exists → buy 1, coins = 3, total = 3.
- Price 3: can afford `3/3 = 1`, 1 exists → buy 1, coins = 0, total = 4.
- Price 4: can afford `0/4 = 0` → stop. Answer: **4**.

This is a satisfying problem — the greedy insight is intuitive once you see it, and the counting-sort twist is a clean exercise in recognizing when bounded inputs let you beat O(n log n).

## Complexity Analysis

Time Complexity: O(n + M), where `n` is the number of bars and `M` is the maximum cost value (here at most 10^5). Building the frequency table is O(n), and scanning the price buckets is O(M).

Space Complexity: O(M) for the frequency array sized to the maximum cost.

## Edge Cases

- **Cannot afford any bar** (e.g., Example 2, all costs exceed `coins`): the loop never buys anything and returns 0. Make sure the affordability check correctly yields 0 here.
- **Can afford every bar** (Example 3): the budget is large enough that `count[p]` is always the binding limit, so every bar is bought.
- **Single bar** (`n == 1`): either you can afford it (return 1) or you cannot (return 0).
- **Duplicate prices**: many bars share the same price; the frequency table naturally handles this by bucketing, and you must cap purchases at the available count, not just affordability.
- **Empty input** (`len(costs) == 0`): defensively returns 0, since there is nothing to buy. (LeetCode guarantees `n >= 1`, but the code handles it cleanly.)
65 changes: 65 additions & 0 deletions problems/1833-maximum-ice-cream-bars/problem.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
---
number: "1833"
frontend_id: "1833"
title: "Maximum Ice Cream Bars"
slug: "maximum-ice-cream-bars"
difficulty: "Medium"
topics:
- "Array"
- "Greedy"
- "Sorting"
- "Counting Sort"
acceptance_rate: 7537.5
is_premium: false
created_at: "2026-06-21T05:24:15.805634+00:00"
fetched_at: "2026-06-21T05:24:15.805634+00:00"
link: "https://leetcode.com/problems/maximum-ice-cream-bars/"
date: "2026-06-21"
---

# 1833. Maximum Ice Cream Bars

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 _the**maximum** number of ice cream bars the boy can buy with _`coins` _coins._

You must solve the problem by counting sort.



**Example 1:**


**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:**


**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:**


**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 <= 105`
* `1 <= costs[i] <= 105`
* `1 <= coins <= 108`
51 changes: 51 additions & 0 deletions problems/1833-maximum-ice-cream-bars/solution.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package main

// Maximum Ice Cream Bars (LeetCode 1833)
//
// Approach: greedy + counting sort. Buying the cheapest bars first maximizes
// the number purchased. Since costs are bounded (1 <= costs[i] <= 10^5), we use
// counting sort: bucket bars by price into a frequency array, then walk prices
// from cheapest to most expensive, buying as many as the budget allows at each
// level. Runs in O(n + maxCost) time.
func maxIceCream(costs []int, coins int) int {
if len(costs) == 0 {
return 0
}

// Find the maximum cost to size the frequency table.
maxCost := 0
for _, c := range costs {
if c > maxCost {
maxCost = c
}
}

// count[p] is the number of bars priced at p.
count := make([]int, maxCost+1)
for _, c := range costs {
count[c]++
}

bought := 0
for price := 1; price <= maxCost; price++ {
if count[price] == 0 {
continue
}

// How many bars at this price can we afford?
affordable := coins / price
if affordable == 0 {
// Cannot afford even one at this price; all higher prices are
// also unaffordable, so we can stop early.
break
}
if affordable > count[price] {
affordable = count[price]
}

bought += affordable
coins -= affordable * price
}

return bought
}
30 changes: 30 additions & 0 deletions problems/1833-maximum-ice-cream-bars/solution_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package main

import "testing"

func TestMaxIceCream(t *testing.T) {
tests := []struct {
name string
costs []int
coins int
expected int
}{
{"example 1: buy four cheapest bars", []int{1, 3, 2, 4, 1}, 7, 4},
{"example 2: cannot afford any bar", []int{10, 6, 8, 7, 7, 8}, 5, 0},
{"example 3: afford all bars", []int{1, 6, 3, 1, 2, 5}, 20, 6},
{"edge case: empty input", []int{}, 100, 0},
{"edge case: single bar affordable", []int{5}, 5, 1},
{"edge case: single bar unaffordable", []int{5}, 4, 0},
{"edge case: all same price, partial buy", []int{2, 2, 2, 2}, 5, 2},
{"edge case: exact budget for all", []int{3, 3, 3}, 9, 3},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := maxIceCream(tt.costs, tt.coins)
if result != tt.expected {
t.Errorf("maxIceCream(%v, %d) = %d, want %d", tt.costs, tt.coins, result, tt.expected)
}
})
}
}