Skip to content

Commit ffdf536

Browse files
committed
Add recursive solution for ATM task
1 parent 340892a commit ffdf536

File tree

3 files changed

+147
-1
lines changed

3 files changed

+147
-1
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ they contain enough code which describes implementation in a natural way.
168168
| Поиск слова в матрице букв (leetcode) | [Youtube](https://youtu.be/FsKU04anMtE) | [Code](src/main/java/by/andd3dfx/search/wordsearch/WordSearch.java) |
169169
| Сжатие строки (Яндекс) | [Youtube](https://youtu.be/s3sGF7C6cV8) | [Code](src/main/java/by/andd3dfx/string/MakeStringCompact.java) |
170170
| Самодельный Stack | [Youtube](https://youtu.be/sZ-DrSHhrWc) | [Code](src/main/java/by/andd3dfx/collections/custom/CustomStack.java) |
171-
| Выдача купюр банкоматом (Яндекс) | [Youtube](https://youtu.be/LDKZtDevRRI) | [Code](src/main/java/by/andd3dfx/common/ATM.java) |
171+
| Выдача купюр банкоматом (Яндекс) | [Youtube](https://youtu.be/LDKZtDevRRI) | [Code](src/main/java/by/andd3dfx/common/ATM.java) [Code2](src/main/java/by/andd3dfx/common/ATM2.java) |
172172
| Поиск набора слов в матрице букв (2 решения) (leetcode) | [Youtube](https://youtu.be/DTyMyr6bNGw) | [Code](src/main/java/by/andd3dfx/search/wordsearch/WordSearch_IIUsingWordSearch.java) [Code2](src/main/java/by/andd3dfx/search/wordsearch/WordSearch_IIUsingRecursion.java) |
173173
| Поиск набора слов в матрице букв 2: префиксное дерево (leetcode) | [Youtube](https://youtu.be/CLYbm21pvig) | [Code](src/main/java/by/andd3dfx/search/wordsearch/WordSearch_IIUsingPrefixTree.java) |
174174
| Поиск в строке наиболее длинной подстроки без повторений (leetcode) | [Youtube](https://youtu.be/Jj66XXja4LY) | [Code](src/main/java/by/andd3dfx/string/LongestWordWithoutRepeatingChars.java) |
+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package by.andd3dfx.common;
2+
3+
import java.util.Comparator;
4+
import java.util.HashMap;
5+
import java.util.List;
6+
import java.util.Map;
7+
8+
/**
9+
* <pre>
10+
* Есть банкомат (ATM), который заряжают купюрами.
11+
* Надо реализовать метод withdraw() для выдачи заданной суммы amount имеющимися в банкомате купюрами.
12+
* Метод withdraw() - мутирующий, т.е. меняет состояние банкомата после вызова (кол-во купюр может уменьшиться).
13+
* </pre>
14+
*
15+
* @see <a href="https://youtu.be/LDKZtDevRRI">Video solution</a>
16+
*/
17+
public class ATM2 {
18+
19+
private Map<Integer, Integer> state;
20+
private List<Integer> nominals;
21+
22+
public ATM2(Map<Integer, Integer> state) {
23+
this.state = new HashMap<>(state);
24+
this.nominals = state.keySet().stream()
25+
.sorted(Comparator.reverseOrder()).toList();
26+
}
27+
28+
/**
29+
* Withdraw asked amount using banknotes of ATM
30+
*
31+
* @param amount sum asked to withdraw
32+
* @return map with solution - pairs {banknote nominal->quantity}
33+
*/
34+
public Map<Integer, Integer> withdraw(int amount) {
35+
// Try to make withdraw using banknote of highest nominal,
36+
// in case of fail - try to start from next nominal
37+
for (int i = 0; i < nominals.size(); i++) {
38+
try {
39+
return withdraw(amount, i);
40+
} catch (IllegalStateException ex) {
41+
// do nothing
42+
}
43+
}
44+
throw new IllegalStateException("Could not perform withdraw!");
45+
}
46+
47+
private Map<Integer, Integer> withdraw(int amount, int nominalIndex) {
48+
if (nominalIndex >= nominals.size()) {
49+
throw new IllegalStateException("Could not perform withdraw!");
50+
}
51+
52+
var nominal = nominals.get(nominalIndex);
53+
if (nominal > amount || state.get(nominal) == 0) {
54+
return withdraw(amount, nominalIndex + 1);
55+
}
56+
57+
var result = new HashMap<Integer, Integer>();
58+
int count = amount / nominal;
59+
count = Math.min(count, state.get(nominal));
60+
result.put(nominal, count);
61+
amount -= nominal * count;
62+
63+
if (amount == 0) {
64+
mutateAtm(result);
65+
return result;
66+
}
67+
68+
result.putAll(withdraw(amount, nominalIndex + 1));
69+
return result;
70+
}
71+
72+
private void mutateAtm(Map<Integer, Integer> result) {
73+
for (var nominal : result.keySet()) {
74+
state.put(nominal, state.get(nominal) - result.get(nominal));
75+
}
76+
}
77+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package by.andd3dfx.common;
2+
3+
import org.junit.Before;
4+
import org.junit.Test;
5+
6+
import java.util.Map;
7+
8+
import static org.assertj.core.api.Assertions.assertThat;
9+
import static org.junit.Assert.assertThrows;
10+
11+
public class ATM2Test {
12+
13+
private ATM2 atm;
14+
15+
@Before
16+
public void setUp() throws Exception {
17+
atm = new ATM2(Map.of(
18+
500, 1,
19+
200, 3,
20+
50, 5
21+
));
22+
}
23+
24+
@Test
25+
public void withdraw() {
26+
var result = atm.withdraw(450);
27+
28+
assertThat(result).isEqualTo(Map.of(
29+
200, 2,
30+
50, 1
31+
));
32+
}
33+
34+
@Test
35+
public void withdrawWhenHighGradeBanknotePresentButShouldNotBeUsed() {
36+
// Spend all 50 banknotes
37+
for (int i = 0; i < 5; i++) {
38+
atm.withdraw(50);
39+
}
40+
41+
// Ask 600, ATM has only 500 & 200 banknotes at this moment
42+
var result = atm.withdraw(600);
43+
44+
assertThat(result).isEqualTo(Map.of(
45+
200, 3
46+
));
47+
}
48+
49+
@Test
50+
public void withdrawWhenNoSmallNominalsInATM() {
51+
var thrown = assertThrows(IllegalStateException.class, () -> atm.withdraw(451));
52+
assertThat(thrown.getMessage()).isEqualTo("Could not perform withdraw!");
53+
}
54+
55+
@Test
56+
public void withdrawForConsequentCalls() {
57+
var result = atm.withdraw(650);
58+
assertThat(result).isEqualTo(Map.of(
59+
500, 1,
60+
50, 3
61+
));
62+
63+
var result2 = atm.withdraw(650);
64+
assertThat(result2).isEqualTo(Map.of(
65+
200, 3,
66+
50, 1
67+
));
68+
}
69+
}

0 commit comments

Comments
 (0)