Facebook Pixel

3789. Minimum Cost to Acquire Required Items

MediumGreedyMath
LeetCode ↗

Problem Description

You are given five integers cost1, cost2, costBoth, need1, and need2.

There are three types of items available:

  • An item of type 1 costs cost1 and contributes 1 unit to the type 1 requirement only.
  • An item of type 2 costs cost2 and contributes 1 unit to the type 2 requirement only.
  • An item of type 3 costs costBoth and contributes 1 unit to both the type 1 and type 2 requirements.

You must collect enough items so that:

  • The total contribution toward type 1 is at least need1.
  • The total contribution toward type 2 is at least need2.

In other words, you need to satisfy two separate requirements at the same time. Type 1 and type 2 items each help with only one requirement, while a type 3 item is more flexible because a single purchase counts toward both requirements simultaneously.

Your goal is to choose how many of each item type to buy so that both requirements are met, while spending as little money as possible.

Return an integer representing the minimum possible total cost to achieve these requirements.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

How We Pick the Algorithm

Why Greedy Algorithms?

This problem maps to Greedy Algorithms through a short path in the full flowchart.

Computemax ormin?yesGreedyselection?yesGreedyAlgorithms

Choosing how many type-3 items to buy to minimize total cost involves comparing costBoth against cost1+cost2 at each unit, a greedy trade-off.

Open in Flowchart

Intuition

The key observation is that a type 3 item is special: one purchase satisfies one unit of both requirements at the same time. So the whole decision comes down to figuring out how many type 3 items to buy, and then filling in the rest with cheaper single-type items.

Let's think about the trade-offs. If we buy a type 3 item to cover one unit of both need1 and need2, it costs costBoth. The alternative way to cover one unit of each requirement separately is to buy one type 1 item and one type 2 item, costing cost1 + cost2. This tells us that type 3 items are only worth buying as long as both requirements still have remaining units to satisfy. Once one requirement is fully met, a type 3 item would "waste" half its value on a requirement that's already done.

This reasoning naturally splits the possibilities into a few clean cases:

  1. Never use type 3. Simply buy need1 type 1 items and need2 type 2 items. The cost is need1 * cost1 + need2 * cost2.

  2. Use type 3 to cover everything. Since each type 3 item covers one unit of both requirements, we need enough of them to satisfy the larger requirement. Buying max(need1, need2) type 3 items covers both needs completely. The cost is costBoth * max(need1, need2).

  3. Use type 3 only while both requirements overlap. Let mn = min(need1, need2). We use mn type 3 items to knock out the shared portion of both requirements, then cover whatever is left of the larger requirement with the matching single-type items. The cost is costBoth * mn + (need1 - mn) * cost1 + (need2 - mn) * cost2.

Why are these three cases enough? Because the cost as a function of "number of type 3 items used" is linear within the meaningful range, so the best choice always lands at one of the boundary points: using zero type 3 items, using just enough to cover the overlap (min), or using enough to cover everything (max). The optimal answer must be one of these endpoints, so we just compute all three and take the minimum among them.

Pattern Learn more about Greedy and Math patterns.

Solution Approach

We use case analysis to directly compute the cost of each candidate strategy, then take the minimum. No loops or extra data structures are needed — just a few arithmetic expressions based on the three boundary cases identified in the intuition.

Case 1 — Only buy Type 1 and Type 2 items.

We ignore type 3 entirely and satisfy each requirement on its own:

a = need1 * cost1 + need2 * cost2

Case 2 — Only buy Type 3 items.

Each type 3 item covers one unit of both requirements, so we need as many as the larger requirement to fully cover both:

b = costBoth * max(need1, need2)

Case 3 — Buy Type 3 items for the overlap, then fill the rest.

Let mn = min(need1, need2). We use mn type 3 items to cover the shared portion of both requirements, then buy single-type items for whatever remains on each side:

c = costBoth * mn + (need1 - mn) * cost1 + (need2 - mn) * cost2

Notice that exactly one of (need1 - mn) or (need2 - mn) will be zero (whichever requirement is the smaller one), so only the leftover of the larger requirement actually contributes to the cost.

Final answer.

We return the smallest of the three computed costs:

return min(a, b, c)

In the implementation, these map directly to the variables in the code:

a = need1 * cost1 + need2 * cost2
b = costBoth * max(need1, need2)
mn = min(need1, need2)
c = costBoth * mn + (need1 - mn) * cost1 + (need2 - mn) * cost2
return min(a, b, c)

Since every step is a constant number of arithmetic operations, the time complexity is O(1) and the space complexity is O(1).

Example Walkthrough

Let's trace through a small concrete example to see how the three cases play out.

Input:

  • cost1 = 4 (price of a type 1 item)
  • cost2 = 3 (price of a type 2 item)
  • costBoth = 5 (price of a type 3 item, covers both)
  • need1 = 3 (need at least 3 units toward requirement 1)
  • need2 = 5 (need at least 5 units toward requirement 2)

We compute the cost for each of the three candidate strategies and take the minimum.

Case 1 — Only buy Type 1 and Type 2 items.

We satisfy each requirement independently with its matching single-type item:

a = need1 * cost1 + need2 * cost2
a = 3 * 4 + 5 * 3
a = 12 + 15
a = 27

So buying 3 type-1 items and 5 type-2 items costs 27.

Case 2 — Only buy Type 3 items.

Each type 3 item adds one unit to both requirements. To satisfy the larger requirement (need2 = 5), we buy max(3, 5) = 5 of them. Those 5 items also fully cover need1 = 3 (with 2 units "spilling over"):

b = costBoth * max(need1, need2)
b = 5 * max(3, 5)
b = 5 * 5
b = 25

So buying 5 type-3 items costs 25.

Case 3 — Buy Type 3 for the overlap, then fill the rest.

The overlap is mn = min(3, 5) = 3. We use 3 type-3 items to knock out the shared portion of both requirements at once. After that:

  • Requirement 1 is fully met (need1 - mn = 3 - 3 = 0 leftover).
  • Requirement 2 still needs need2 - mn = 5 - 3 = 2 more units, filled with type-2 items.
c = costBoth * mn + (need1 - mn) * cost1 + (need2 - mn) * cost2
c = 5 * 3 + (3 - 3) * 4 + (5 - 3) * 3
c = 15 + 0 + 6
c = 21

So buying 3 type-3 items plus 2 type-2 items costs 21.

Final answer.

We take the minimum of the three candidates:

min(a, b, c) = min(27, 25, 21) = 21

The optimal strategy is Case 3: cover the overlapping 3 units with the flexible type-3 items, then top up the larger requirement's remaining 2 units with cheap type-2 items. This beats both the "pure single-type" plan (27) and the "all type-3" plan (25), confirming that the best solution always lands at one of these three boundary points.

Solution Implementation

1class Solution:
2    def minimumCost(
3        self, cost1: int, cost2: int, cost_both: int, need1: int, need2: int
4    ) -> int:
5        # Strategy A: buy every item individually, no bundles used.
6        buy_individually = need1 * cost1 + need2 * cost2
7
8        # Strategy B: cover both needs using only bundles.
9        # Buying max(need1, need2) bundles satisfies both requirements,
10        # since each bundle provides one of each type (surplus is acceptable).
11        buy_only_bundles = cost_both * max(need1, need2)
12
13        # Strategy C: use bundles for the overlapping (smaller) amount,
14        # then fill the remaining items of the larger requirement individually.
15        min_need = min(need1, need2)
16        buy_mixed = (
17            cost_both * min_need
18            + (need1 - min_need) * cost1
19            + (need2 - min_need) * cost2
20        )
21
22        # Return the cheapest of the three strategies.
23        return min(buy_individually, buy_only_bundles, buy_mixed)
24
1class Solution {
2    public long minimumCost(int cost1, int cost2, int costBoth, int need1, int need2) {
3        // Strategy 1: buy every item separately, never using the paired purchase.
4        long buySeparately = (long) need1 * cost1 + (long) need2 * cost2;
5
6        // Strategy 2: use paired purchases to cover the larger of the two demands
7        // entirely (each pair satisfies one item of each type, so the larger demand
8        // determines how many pairs are needed; the excess of the other type comes free).
9        long pairCoverMax = (long) costBoth * Math.max(need1, need2);
10
11        // Strategy 3: use paired purchases only for the overlapping (smaller) demand,
12        // then buy the remaining items of the larger-demand type separately.
13        int minNeed = Math.min(need1, need2);
14        long pairCoverMin = (long) costBoth * minNeed
15                + (long) (need1 - minNeed) * cost1
16                + (long) (need2 - minNeed) * cost2;
17
18        // Return the cheapest of the three strategies.
19        return Math.min(buySeparately, Math.min(pairCoverMax, pairCoverMin));
20    }
21}
22
1class Solution {
2public:
3    long long minimumCost(int cost1, int cost2, int costBoth, int need1, int need2) {
4        // Strategy 1: Buy each item separately, no combined purchases.
5        // Pay cost1 for every item of type 1 and cost2 for every item of type 2.
6        long long buySeparately = 1LL * need1 * cost1 + 1LL * need2 * cost2;
7
8        // Strategy 2: Use only combined purchases to cover both demands.
9        // Each combined purchase provides one of each type, so we need
10        // max(need1, need2) combined purchases to satisfy the larger demand.
11        long long buyAllCombined = 1LL * costBoth * max(need1, need2);
12
13        // Strategy 3: Combine the overlapping portion, then buy the remainder.
14        // Use combined purchases for the common amount min(need1, need2),
15        // then buy the leftover of each type separately.
16        int minNeed = min(need1, need2);
17        long long buyMixed = 1LL * costBoth * minNeed
18            + 1LL * (need1 - minNeed) * cost1
19            + 1LL * (need2 - minNeed) * cost2;
20
21        // Return the cheapest of the three strategies.
22        return min({buySeparately, buyAllCombined, buyMixed});
23    }
24};
25
1/**
2 * Calculate the minimum cost to obtain the required number of each item.
3 *
4 * Purchase options:
5 *  - Buy a single item of type 1 for `cost1`
6 *  - Buy a single item of type 2 for `cost2`
7 *  - Buy a bundle containing one of each item for `costBoth`
8 *
9 * @param cost1    Cost of buying one item of type 1 alone
10 * @param cost2    Cost of buying one item of type 2 alone
11 * @param costBoth Cost of buying a bundle (one of each item)
12 * @param need1    Required quantity of item type 1
13 * @param need2    Required quantity of item type 2
14 * @returns The minimum total cost to satisfy both requirements
15 */
16function minimumCost(
17    cost1: number,
18    cost2: number,
19    costBoth: number,
20    need1: number,
21    need2: number,
22): number {
23    // Strategy A: Buy every item individually, no bundles.
24    const buyAllSeparately = need1 * cost1 + need2 * cost2;
25
26    // Strategy B: Use enough bundles to cover the larger requirement entirely.
27    // The smaller requirement is automatically satisfied by the bundle overflow.
28    const coverWithBundles = costBoth * Math.max(need1, need2);
29
30    // Strategy C: Use bundles only for the overlapping count (the smaller need),
31    // then buy the leftover of the larger need individually.
32    const overlapCount = Math.min(need1, need2);
33    const bundlePlusRemainder =
34        costBoth * overlapCount +
35        (need1 - overlapCount) * cost1 +
36        (need2 - overlapCount) * cost2;
37
38    // Return the cheapest of the three strategies.
39    return Math.min(buyAllSeparately, coverWithBundles, bundlePlusRemainder);
40}
41

Time and Space Complexity

Time Complexity: O(1)

The function performs only a fixed number of arithmetic operations regardless of the input values:

  • a = need1 * cost1 + need2 * cost2 — two multiplications and one addition, all constant time.
  • b = costBoth * max(need1, need2) — one call to max on two values (constant time) and one multiplication.
  • mn = min(need1, need2) — one call to min on two values, constant time.
  • c = costBoth * mn + (need1 - mn) * cost1 + (need2 - mn) * cost2 — a fixed set of multiplications, subtractions, and additions.
  • return min(a, b, c)min over three fixed values, constant time.

Since none of these operations depend on the size of any input (there are no loops or recursive calls), the total work is bounded by a constant. Therefore the time complexity is O(1).

Space Complexity: O(1)

The algorithm only allocates a constant number of scalar variables (a, b, mn, c) and does not use any auxiliary data structures whose size grows with the input. Hence the space complexity is O(1).

Pattern Learn more about how to find time and space complexity quickly.

Common Pitfalls

Pitfall 1 — Assuming bundles are only worth buying when cost_both <= cost1 + cost2

A natural but flawed instinct is to think: "A bundle replaces one type 1 and one type 2 item, so it's only useful when cost_both <= cost1 + cost2. Otherwise, just buy everything individually."

This reasoning misses Strategy B (buy only bundles). Even when a bundle is more expensive than buying one type 1 plus one type 2 item, it can still be the cheapest overall option. Consider:

  • cost1 = 1, cost2 = 100, cost_both = 5, need1 = 1, need2 = 10

Here cost_both (5) > cost1 + cost2 (101)? No — but flip it: imagine the individual cost of the larger requirement is dominated by an expensive single type. Buying max(need1, need2) bundles can beat paying the full need2 * cost2 price for the second requirement, because the surplus type 1 coverage is free (already paid for by the bundle).

Solution: Always compute and compare all three strategies. Do not prematurely prune any case with a cost_both <= cost1 + cost2 shortcut. The three formulas already cover every meaningful trade-off, so trust the min() of all three.


Pitfall 2 — Believing Strategy C is always covered by A and B (or vice versa)

It is tempting to keep only two strategies, thinking the third is redundant. It is not. Each strategy can be uniquely optimal:

  • A wins when bundles are too expensive relative to both individual costs.
  • B wins when bundles are cheap and the two needs are close in size (surplus is negligible).
  • C wins when bundles are cheap for the shared portion, but the leftover of the larger requirement is cheaper to buy individually than as extra bundles.

Dropping any one of them produces wrong answers on edge inputs.

Solution: Keep all three formulas. The cost of maintaining them is trivial (O(1)), and each guards against a distinct failure mode.


Pitfall 3 — Confusing which need has leftover in Strategy C

In Strategy C, the code subtracts min_need from both need1 and need2. A common mistake is to manually assume the leftover always belongs to a specific need (e.g., always need1), or to only add the leftover for the larger one and forget the formula handles both terms generically.

# WRONG — assumes need1 is always the larger one
buy_mixed = cost_both * min_need + (need1 - min_need) * cost1

If need2 > need1, this drops the entire leftover of need2, drastically underestimating the cost.

Solution: Subtract min_need from both needs and add both leftover terms, as the original code does. Exactly one term is zero automatically — no manual branching on which need is larger is required:

buy_mixed = (
    cost_both * min_need
    + (need1 - min_need) * cost1
    + (need2 - min_need) * cost2
)

Pitfall 4 — Integer overflow in other languages

In Python this is a non-issue, but if porting to Java/C++, products like need1 * cost1 can overflow 32-bit int when the constraints allow large values (e.g., needs and costs up to 10^6, giving products near 10^{12}).

Solution: Use 64-bit integers (long in Java, long long in C++) for all intermediate products and sums when translating this logic to a fixed-width integer language.

Ready to land your dream job?

Unlock your dream job with a 5-minute quiz for a personalized study roadmap!

Get My Roadmap
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Get a Personalized Study Roadmap:

What is the best way of checking if an element exists in a sorted array once in terms of time complexity? Select the best that applies.


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More