Buenas, en ese caso, lo que tendrias que hacer es recorrer cada una de las denominaciones, de la mayor a la menor. En cada vuelta, deberías restar el valor de esa denominación, si todavía quedan tickets disponibles y si el valor del ticket no es mayor a lo que falta para cubrir el total. Y básicamente, es eso. Necesitás un par de variables de control y algunos contadores y la base del esquema estaría.
 
Un ejemplo de esto aplicado (lo probé con un par de valores y funciona, pero lo hice al vuelo así que puede tener algunos errores o puede no ser lo más óptimo; el $montoMaximo te convendría tomarlo dinámicamente)  
 Código PHP:
    <?php
$tickets[] = array('valor'=>1,'cantidad'=>10);     //     10
$tickets[] = array('valor'=>2,'cantidad'=>10);     //     20
$tickets[] = array('valor'=>5,'cantidad'=>10);     //     50
$tickets[] = array('valor'=>10,'cantidad'=>10); //    100
$tickets[] = array('valor'=>20,'cantidad'=>10); //     200
$tickets[] = array('valor'=>50,'cantidad'=>10); //    500
$montoMaximo = 890;
$montoRequerido = 538;
$checkSuma = 0;
$tab = '    ';
$resultado = array();
for ($i=count($tickets)-1;$i>=0;$i--) {
    $arrTicketActual = $tickets[$i];
    $cantidad         = $arrTicketActual['cantidad'];
    $valor             = $arrTicketActual['valor'];
//    echo "VALOR::$valor<br>";
    
    $cantTicketsValor = 0;
    // mientras:
    //        1) haya tickets
    //        2) el valor del ticket sea menor o igual a lo que falta para completar lo pedido
    //        3) lo acumulado sea menor a lo pedido
    while ($cantidad > 0 && $montoRequerido - $checkSuma >= $valor && $checkSuma < $montoRequerido) {
        $checkSuma += $valor;
        $cantidad--;
        $cantTicketsValor++;
//        echo "$tab" . "CANTIDAD: $cantidad <br>";
    
    }
    
    if ($cantTicketsValor > 0) {
        $resultado[] = array('valor'=>$valor,'cantidad'=>$cantTicketsValor);
    }
}
if ($checkSuma !== $montoRequerido) {
    echo "NO SE PUDO ENTREGAR LA SUMA PEDIDA<br>";
    echo "suma entregada:$checkSuma<br>";
    echo "suma pedida:$montoRequerido<br>";
} else {
    echo "OK:<br>";
    foreach($resultado as $r) {
        echo "$tab" . "Valor Ticket:{$r['valor']}<br>";
        echo "$tab" . "Cantidad Ticket:{$r['cantidad']}<br>";
    }
    
    
}
/**
echo '<pre>';
print_r($resultado);
echo '</pre>';
*/
?>    
  
Un tema: Tendrías que ver si necesitás más validaciones y, si estás usando base de datos, tené en cuenta que no va a ser realmente "seguro" a menos que uses transacciones (creo que en mysql se puede hacer a partir de la versión 5, este artículo explica el problema --en inglés--: 
http://www.samspublishing.com/articl...p=29312&rl=1); La idea sería bloquear las tablas mientras estás haciendo las cuentas. Podría pasar (al menos en teoría, y creo que tendrías que tener bastante mala leche) una situación como la siguiente. Supongamos que el usuario A pide $100. Hay $150 en total, todos tickets de $50 (para simplificar). Mientras estás haciendo los cálculos, entra el usuario B y pide también $100. Pero la consulta a la base va a devolver que hay $150 disponibles, porque como está procesándose, el pedido de A no hizo ninguna actualización en la base.  
Entonces, terminás de calcular el pedido A, le das $100 y descontás 2 tickets de la base. Pero cuando termines de calcular el pedido de B, también le vas a dar $100, cuando en realidad sólo te quedan $50.  
Bueno, todo esto en realidad es hilar más bien fino, si no estás haciendo algo tipo home banking o por el estilo, un error de este tipo tal vez sea un probabilidad (baja) aceptable. 
Suerte
Califa