Эта проблема требует простого рекурсивного недетерминированного программирования.
- Мы начинаем с заданной суммы и заданного списка банкнот достоинств, очевидно, с неограниченным количеством каждой банкноты (в противном случае это была бы другая проблема).
- В каждый момент времени мы можем использовать либо самую большую купюру, либо нет.
- Если мы его используем, общая сумма уменьшается на сумму счета.
- Если сумма равна 0, мы получили решение!
- Если сумма отрицательная, она недействительна, поэтому нам следует отказаться от этого пути.
Код здесь будет следовать за другим моим ответом, в котором из общего количества решений (которых больше одного, для вашего примера). Нам просто нужно будет помнить и о самих решениях, тогда как упомянутый выше код только их считал.
Мы можем закодировать это как recursive-backtracking, вызывая обратный вызов с каждым успешно найденным решением изнутри самого глубокого уровня рекурсии (равносильно самому глубоко вложенному циклу в структуре вложенных циклов, созданной с помощью рекурсии , что является сутью рекурсивного поиска с возвратом):
(define (change sum bills callback)
(let loop ([sum sum] [sol '()] [bills bills]) ; "sol" for "solution"
(cond
((zero? sum) (callback sol)) ; process a solution found
((< sum 0) #f)
((null? bills) #f)
(else
(apply
(lambda (b . bs) ; the "loop":
;; 1. ; either use the first
(loop (- sum b) (cons b sol) bills) ; denomination,
;; 2. ; or,
(loop sum sol bs)) ; after backtracking, don't!
bills)))))
Он должен быть вызван, например, один из
;; construct `the-callback` for `solve` and call
;; (solve ...params the-callback)
;; where `the-callback` is an exit continuation
(define (first-solution solve . params)
(call/cc (lambda (return)
(apply solve (append params ; use `return` as
(list return)))))) ; the callback
(define (n-solutions n solve . params) ; n assumed an integer
(let ([res '()]) ; n <= 0 gets ALL solutions
(call/cc (lambda (break)
(apply solve (append params
(list (lambda (sol)
(set! res (cons sol res))
(set! n (- n 1))
(cond ((zero? n) (break)))))))))
(reverse res)))
Тестирование,
> (first-solution change 406 (list 100 10 5 2))
'(2 2 2 100 100 100 100)
> (n-solutions 7 change 415 (list 100 10 5 2 1))
'((5 10 100 100 100 100)
(1 2 2 10 100 100 100 100)
(1 1 1 2 10 100 100 100 100)
(1 1 1 1 1 10 100 100 100 100)
(5 5 5 100 100 100 100)
(1 2 2 5 5 100 100 100 100)
(1 1 1 2 5 5 100 100 100 100))
Относительно того, как структурирован этот код, см. Как сгенерировать все перестановки элементов в списке по одной в Лиспе? Он создает вложенные циклы с решением доступный в теле самого внутреннего цикла.
Относительно того, как кодировать недетерминированный алгоритм (делая все возможные варианты сразу) надлежащим функциональным способом, см. Как сделать powerset в DrRacket? и Как найти разделы списка в схеме.
person
Will Ness
schedule
29.04.2018
set!
? - person Will Ness   schedule 29.04.2018