Lección 16 · 16 de 20
Recursión y backtracking · Pascal
Resuelva problemas de búsqueda con recursión y backtracking en Pascal: qué facturas pendientes suman exactamente el importe de un pago.

Un cliente paga 500 EUR y hay seis facturas pendientes: ¿qué combinaciones suman justo ese importe? Es un caso real de conciliación bancaria y un ejemplo perfecto de backtracking (vuelta atrás): probar una opción, seguir explorando y, si no lleva a nada, deshacerla y probar la siguiente.
Search(i, remaining) decide sobre la factura i sabiendo cuánto falta por cubrir. Tiene dos casos base, los que detienen la recursión: si remaining llega a 0, la selección actual es una solución y se escribe; si se acaban las facturas o el importe se pasa (queda negativo), ese camino es un callejón sin salida y se vuelve atrás con Exit.
El corazón está en las cuatro últimas líneas: se elige la factura (chosen[i] := True) y se explora el resto con lo que falta; después se deshace la elección (chosen[i] := False) y se explora el resto sin ella. Así se recorren todas las combinaciones, 2⁶ = 64 como mucho, y se descartan pronto las que se pasan.
El array chosen es el estado compartido que se va marcando y desmarcando, y found cuenta las soluciones; Free Pascal permite inicializar una variable global al declararla, como en found: Integer = 0. El resultado: cuatro maneras distintas de casar el pago.
Código · main.pas
program main;const Invoices: array[0..5] of Integer = (120, 340, 90, 160, 250, 40); Payment = 500;var chosen: array[0..5] of Boolean; found: Integer = 0;procedure Search(i, remaining: Integer);var k: Integer;begin if remaining = 0 then // a match: print the chosen invoices begin Inc(found); Write('Option ', found, ':'); for k := 0 to High(Invoices) do if chosen[k] then Write(' ', Invoices[k]); WriteLn; Exit; end; if (i > High(Invoices)) or (remaining < 0) then Exit; // dead end: go back chosen[i] := True; // try with invoice i... Search(i + 1, remaining - Invoices[i]); chosen[i] := False; // ...undo, and try without it Search(i + 1, remaining);end;begin Search(0, Payment); WriteLn(found, ' ways to match a payment of ', Payment);end.Salida · fpc main.pas && ./main
Option 1: 120 340 40 Option 2: 120 90 250 40 Option 3: 340 160 Option 4: 90 160 250 4 ways to match a payment of 500
Para recordar
- Una función recursiva se llama a sí misma con un problema más pequeño.
- Los casos base detienen la recursión.
- Backtracking: elegir, explorar y deshacer.
- Cortar pronto los caminos imposibles ahorra trabajo.
- El estado compartido se marca antes y se desmarca después.
Ejercicio
Haga que la búsqueda se detenga en cuanto encuentre la primera combinación y que, en lugar de las facturas, escriba sus posiciones en el array.