@@ -22,6 +22,7 @@ public class Atm {
2222
2323 public Atm (Map <Integer , Integer > state ) {
2424 this .state = new HashMap <>(state );
25+ // Порядок важен для backtracking: сначала пробуем крупные номиналы.
2526 this .nominals = state .keySet ().stream ()
2627 .sorted (Comparator .reverseOrder ()).toList ();
2728 }
@@ -33,44 +34,48 @@ public Atm(Map<Integer, Integer> state) {
3334 * @return map with solution - pairs {banknote nominal->quantity}
3435 */
3536 public Map <Integer , Integer > withdraw (int amount ) {
36- // Try to make withdraw using banknote of highest nominal,
37- // in case of fail - try to start from next nominal
38- for (int i = 0 ; i < nominals .size (); i ++) {
39- try {
40- return withdraw (amount , i );
41- } catch (IllegalStateException ex ) {
42- // do nothing
43- }
37+ if (amount == 0 ) {
38+ return Map .of ();
39+ }
40+
41+ // Перебор всех комбинаций (не жадный): для каждого номинала пробуем 0..max купюр.
42+ var result = new HashMap <Integer , Integer >();
43+ if (findSolution (amount , 0 , result )) {
44+ mutateAtm (result );
45+ return result ;
4446 }
4547
4648 throw new IllegalStateException ("Could not perform withdraw!" );
4749 }
4850
49- private Map <Integer , Integer > withdraw (int amount , int nominalIndex ) {
50- var result = new HashMap <Integer , Integer >();
51+ /**
52+ * Рекурсивный поиск комбинации купюр для {@code amount}, начиная с номинала {@code index}.
53+ * Найденное решение накапливается в {@code result}.
54+ */
55+ private boolean findSolution (int amount , int index , Map <Integer , Integer > result ) {
56+ if (amount == 0 ) {
57+ return true ;
58+ }
59+ if (index >= nominals .size ()) {
60+ return false ; // купюры закончились, сумма не набрана
61+ }
5162
52- for (var index = nominalIndex ; index < nominals .size (); index ++) {
53- var nominal = nominals .get (index );
54- if (nominal > amount || state .get (nominal ) == 0 ) {
55- continue ;
63+ var nominal = nominals .get (index );
64+ // Сколько купюр этого номинала можно взять: min(есть в банкомате, amount / nominal).
65+ int maxCount = Math .min (state .get (nominal ), amount / nominal );
66+ // От max к 0: сначала варианты с большим числом крупных купюр (см. тесты).
67+ for (int count = maxCount ; count >= 0 ; count --) {
68+ if (count > 0 ) {
69+ result .put (nominal , count );
5670 }
57-
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- break ;
71+ if (findSolution (amount - nominal * count , index + 1 , result )) {
72+ return true ;
73+ }
74+ if (count > 0 ) {
75+ result .remove (nominal ); // backtrack
6576 }
6677 }
67-
68- if (amount > 0 ) {
69- throw new IllegalStateException ("Could not perform withdraw!" );
70- }
71-
72- mutateAtm (result );
73- return result ;
78+ return false ;
7479 }
7580
7681 private void mutateAtm (Map <Integer , Integer > result ) {
0 commit comments