Give Exact Change at a Ticket Kiosk
Solve this ProblemA parking-ticket kiosk sells tickets that each cost 4. Customers line up and each pays with exactly one note: a 4 (no change needed), an 8 (change of 4) or a 20 (change of 16). The kiosk starts empty and can only give change using notes it has already collected from earlier customers. Decide whether every customer can be given exact change.
A 20 can be changed in several ways (two 8s, an 8 and two 4s, or four 4s), and trying all of them can branch a lot. A greedy rule removes the branching: 4-notes are useful for every kind of customer, so prefer paying 20s with 8-notes and save the 4s.
Test Case 1:
Test Case 2:
Test Case 3:
Constraints
- ◆
1 ≤ payments.length ≤ 14 - ◆
Every ticket costs 4. Each customer pays with exactly one note, and every payments[i] is 4, 8 or 20 - ◆
The kiosk starts with no notes at all and can only give change using notes it has already collected from earlier customers (a 20 note is never used as change) - ◆
Customers are served in order. Return true if every customer can be given exact change, otherwise false
Try the Dry Run
Don't just read the solution — watch it execute, one step at a time.
Approach & Solutions
Brute Force — Try Every Way to Give the Change
BruteServe the customers in order while tracking how many 4-notes and 8-notes the kiosk holds. A customer paying 4 needs no change; paying 8 needs one 4 back. A customer paying 20 needs 16 back, which can be made three ways — two 8s, one 8 and two 4s, or four 4s — so try each way the kiosk can actually afford and recurse on the rest; if none of them lets the remaining customers finish, this customer cannot be served. This is guaranteed to find a way if any exists, but each 20 can branch three ways.
O(3ᵏ) for k customers paying 20O(n)1class Solution {
2 public boolean canGiveChange(int[] payments) {
3 return serve(payments, 0, 0, 0);
4 }
5
6 private boolean serve(int[] payments, int index, int fours, int eights) {
7 if (index == payments.length) return true;
8 int pay = payments[index];
9 if (pay == 4) return serve(payments, index + 1, fours + 1, eights);
10 if (pay == 8) return fours >= 1 && serve(payments, index + 1, fours - 1, eights + 1);
11 for (int useEights = 0; useEights <= 2; useEights++) {
12 int needFours = (16 - 8 * useEights) / 4;
13 if (useEights <= eights && needFours <= fours
14 && serve(payments, index + 1, fours - needFours, eights - useEights)) return true;
15 }
16 return false;
17 }
18}Optimal — Count Notes and Always Prefer the 8s
OptimalKeep just two counters, fours and eights. For a 20 the change of 16 should be made with as many 8s as possible: an 8-note can only ever help as change for a 20-payer, while 4-notes are useful for both 8-payers and 20-payers, so the 4-notes are the scarcer resource and should be saved for later. Concretely: pay a 20 with two 8s if possible; otherwise with one 8 and two 4s; otherwise with four 4s; otherwise fail. A single pass, no recursion and no backtracking.
O(n)O(1)1class Solution {
2 public boolean canGiveChange(int[] payments) {
3 int fours = 0, eights = 0;
4 for (int pay : payments) {
5 if (pay == 4) {
6 fours++;
7 } else if (pay == 8) {
8 if (fours == 0) return false;
9 fours--;
10 eights++;
11 } else {
12 if (eights >= 2) {
13 eights -= 2;
14 } else if (eights == 1 && fours >= 2) {
15 eights--;
16 fours -= 2;
17 } else if (fours >= 4) {
18 fours -= 4;
19 } else {
20 return false;
21 }
22 }
23 }
24 return true;
25 }
26}