diff --git a/problems/2144-minimum-cost-of-buying-candies-with-discount/analysis.md b/problems/2144-minimum-cost-of-buying-candies-with-discount/analysis.md new file mode 100644 index 0000000..2f7cd4f --- /dev/null +++ b/problems/2144-minimum-cost-of-buying-candies-with-discount/analysis.md @@ -0,0 +1,53 @@ +# 2144. Minimum Cost of Buying Candies With Discount + +[LeetCode Link](https://leetcode.com/problems/minimum-cost-of-buying-candies-with-discount/) + +Difficulty: Easy +Topics: Array, Greedy, Sorting +Acceptance Rate: 66.3% + +## Hints + +### Hint 1 + +Think about which candy you would *want* to get for free. The free candy must be less than or equal to the cheaper of the two you pay for, so a free candy can never be the most expensive one in its group of three. What ordering of the array makes it easy to reason about "the cheapest in a group of three"? + +### Hint 2 + +After sorting, consider processing candies from the most expensive to the cheapest. If you pick the two most expensive remaining candies as the "paid" pair, which candy can you legally claim for free? The structure of the problem strongly suggests grouping the sorted list into chunks of three. + +### Hint 3 + +Sort the array in descending order. Iterate through it and, for every group of three consecutive candies, pay for the first two and skip the third. The third candy in each group is guaranteed to be the cheapest of that triple, and by greedy exchange, no other pairing can save more money. Any leftover 1 or 2 candies at the end must all be paid for in full. + +## Approach + +The discount lets us get one candy free for every two we buy, with the constraint that the free candy's cost cannot exceed the minimum cost of the two purchased candies. To minimize the total bill we want the candies we skip paying for to be as expensive as possible — but the rule caps each freebie at the smaller of its purchase pair, so the best we can ever do is make the freebie equal to (or smaller than) the cheaper purchased candy. + +The clean greedy is: + +1. Sort `cost` in descending order. +2. Walk through the sorted array. For indices `0, 1, 2`, pay for `0` and `1`, get `2` free. For indices `3, 4, 5`, pay for `3` and `4`, get `5` free. And so on — every index whose position is `2 mod 3` is free. +3. Sum the prices of all indices where `i % 3 != 2`. + +Why this is optimal: in the sorted-descending order, the third candy of any group of three is the smallest of those three, so making it the freebie is always legal. And we've maximized the value of the freebie because each freebie is the third-largest unprocessed candy at the time we encounter it — no rearrangement could promote a larger candy into a free slot without violating the "free ≤ min(paid pair)" rule. + +Example with `cost = [6,5,7,9,2,2]`: + +- Sort descending: `[9, 7, 6, 5, 2, 2]`. +- Group 1: pay 9 + 7, get 6 free. +- Group 2: pay 5 + 2, get 2 free. +- Total = 9 + 7 + 5 + 2 = 23. + +## Complexity Analysis + +Time Complexity: O(n log n) — dominated by the sort. +Space Complexity: O(1) extra space if sorting in place (Go's `sort.Sort` on a slice is in place, ignoring sort's internal recursion stack of O(log n)). + +## Edge Cases + +- **`len(cost) == 1`**: only one candy, no pair possible, so pay its full cost. +- **`len(cost) == 2`**: two candies form a pair but there's no third candy to take free; pay for both. +- **All equal costs** (e.g., `[5,5,5]`): the freebie equals the paid candies, which is allowed since the constraint is `≤`, not `<`. +- **Length not divisible by 3** (e.g., 4 or 5 candies): the trailing 1 or 2 candies after the last full triple must all be paid for; the greedy iteration naturally handles this because `i % 3 != 2` for those indices. +- **Minimum constraint values** (length 1, cost 1): the answer is just the single cost. diff --git a/problems/2144-minimum-cost-of-buying-candies-with-discount/problem.md b/problems/2144-minimum-cost-of-buying-candies-with-discount/problem.md new file mode 100644 index 0000000..4337d14 --- /dev/null +++ b/problems/2144-minimum-cost-of-buying-candies-with-discount/problem.md @@ -0,0 +1,71 @@ +--- +number: "2144" +frontend_id: "2144" +title: "Minimum Cost of Buying Candies With Discount" +slug: "minimum-cost-of-buying-candies-with-discount" +difficulty: "Easy" +topics: + - "Array" + - "Greedy" + - "Sorting" +acceptance_rate: 6633.1 +is_premium: false +created_at: "2026-06-01T05:23:28.821637+00:00" +fetched_at: "2026-06-01T05:23:28.821637+00:00" +link: "https://leetcode.com/problems/minimum-cost-of-buying-candies-with-discount/" +date: "2026-06-01" +--- + +# 2144. Minimum Cost of Buying Candies With Discount + +A shop is selling candies at a discount. For **every two** candies sold, the shop gives a **third** candy for **free**. + +The customer can choose **any** candy to take away for free as long as the cost of the chosen candy is less than or equal to the **minimum** cost of the two candies bought. + + * For example, if there are `4` candies with costs `1`, `2`, `3`, and `4`, and the customer buys candies with costs `2` and `3`, they can take the candy with cost `1` for free, but not the candy with cost `4`. + + + +Given a **0-indexed** integer array `cost`, where `cost[i]` denotes the cost of the `ith` candy, return _the**minimum cost** of buying **all** the candies_. + + + +**Example 1:** + + + **Input:** cost = [1,2,3] + **Output:** 5 + **Explanation:** We buy the candies with costs 2 and 3, and take the candy with cost 1 for free. + The total cost of buying all candies is 2 + 3 = 5. This is the **only** way we can buy the candies. + Note that we cannot buy candies with costs 1 and 3, and then take the candy with cost 2 for free. + The cost of the free candy has to be less than or equal to the minimum cost of the purchased candies. + + +**Example 2:** + + + **Input:** cost = [6,5,7,9,2,2] + **Output:** 23 + **Explanation:** The way in which we can get the minimum cost is described below: + - Buy candies with costs 9 and 7 + - Take the candy with cost 6 for free + - We buy candies with costs 5 and 2 + - Take the last remaining candy with cost 2 for free + Hence, the minimum cost to buy all candies is 9 + 7 + 5 + 2 = 23. + + +**Example 3:** + + + **Input:** cost = [5,5] + **Output:** 10 + **Explanation:** Since there are only 2 candies, we buy both of them. There is not a third candy we can take for free. + Hence, the minimum cost to buy all candies is 5 + 5 = 10. + + + + +**Constraints:** + + * `1 <= cost.length <= 100` + * `1 <= cost[i] <= 100` diff --git a/problems/2144-minimum-cost-of-buying-candies-with-discount/solution.go b/problems/2144-minimum-cost-of-buying-candies-with-discount/solution.go new file mode 100644 index 0000000..df145f5 --- /dev/null +++ b/problems/2144-minimum-cost-of-buying-candies-with-discount/solution.go @@ -0,0 +1,19 @@ +package main + +// Greedy: sort costs in descending order, then iterate. For every group of three +// (indices 0-1-2, 3-4-5, ...) we pay for the first two and take the third for free. +// The freebie is always the smallest of its triple, satisfying the discount rule, +// and no other pairing can free up a more expensive candy. + +import "sort" + +func minimumCost(cost []int) int { + sort.Sort(sort.Reverse(sort.IntSlice(cost))) + total := 0 + for i, c := range cost { + if i%3 != 2 { + total += c + } + } + return total +} diff --git a/problems/2144-minimum-cost-of-buying-candies-with-discount/solution_test.go b/problems/2144-minimum-cost-of-buying-candies-with-discount/solution_test.go new file mode 100644 index 0000000..ccfa7bf --- /dev/null +++ b/problems/2144-minimum-cost-of-buying-candies-with-discount/solution_test.go @@ -0,0 +1,36 @@ +package main + +import "testing" + +func TestMinimumCost(t *testing.T) { + tests := []struct { + name string + cost []int + expected int + }{ + {"example 1: three candies", []int{1, 2, 3}, 5}, + {"example 2: six candies with two free", []int{6, 5, 7, 9, 2, 2}, 23}, + {"example 3: two candies pay both", []int{5, 5}, 10}, + {"edge case: single candy", []int{7}, 7}, + {"edge case: four candies, one free", []int{1, 2, 3, 4}, 8}, + {"edge case: all equal costs", []int{5, 5, 5}, 10}, + {"edge case: max constraint length all 100s", func() []int { + c := make([]int, 9) + for i := range c { + c[i] = 100 + } + return c + }(), 600}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + input := make([]int, len(tt.cost)) + copy(input, tt.cost) + got := minimumCost(input) + if got != tt.expected { + t.Errorf("minimumCost(%v) = %d, want %d", tt.cost, got, tt.expected) + } + }) + } +}