Proc by nebylo ? Ted to je O((n+max(pocet_v_baleni))^2)
Pridavam predelany kod ktery respektuje vyse zminenou diskuzi
<?
// definice baleni dle zadani. Klic je pocet ks, hodnota je cena za ks
$baleni=array(1=>20,5=>18,10=>19,50=>17);
// zadany pocet ks
$ks=78;
// priznak zda muzu vyhodit nejake kusy. Napriklad celkova cena pro 49 je vetsi nez pro 50ks
// vyplati se koupit 50 a 1 vyhodit
$muzuvyhazovat=true;
// nadale budu pouzivat baleni od nejvetsiho poctu kusu v baleni. Na objednavce to bude vypadat lepe.
krsort($baleni);
// kontrola jestli je zadane baleni 1ks. To zaruci moznost poskladat jakykoliv pocet ks
// cena za nejmensi baleni - budu ji potrebovat
end($baleni);
$nejmensibalenicena=current($baleni)*key($baleni);
if (!isset($baleni[1])) {
if ($muzuvyhazovat) {
// naplnim baleni mensi nez nejmensi s tim ze se musi vyhodit nejake kusy
for ($i=key($baleni)-1;$i>=1;$i--) {
$baleni[$i]=$nejmensibalenicena;
}
} else {
// zkontroluju zda to muzu vubec dodat z dannych baleni
$deleno=false;
foreach ($baleni as $pocet=>$tmp)
if (!$deleno and ($ks % $pocet==0)) $deleno=true;
if (!$deleno) die("pocet neni mozno poskladat z moznych baleni");
}
}
// pokud je pocet ks vetsi nez nejmensi spolecny nasobek baleni (nsnb), staci pocitat optimalni ceny pro nsbn a zbytek po deleni
// poctu nsbn. Napr. pokud chci 738 baleni a nsnb je 70, tak vysledek bude 10*cena(nsnb)+cena(38)
// Proc? protoze mnozstvi nsbn lze poskladat z kterehokoliv baleni, lze ho poskladat i z baleni s nejnizi cenou za kus
// nebudu tady psat eukliduv algoritmus at to neni dlouhe, spokojim se s kratkym orientacnim vypoctem. Kdyztak si ho sem dopiste.
$nsnb=1;
foreach ($baleni as $pocet=>$tmp)
if (($nsnb % $pocet) !=0) $nsnb*=$pocet;
// pocet kombinaci ktery musim spocitat. Je to bud ks nebo nsbn - co je mensi
$n=$ks<$nsnb?$ks:$nsnb;
// jestli muzu vyhazovat, pridam jeste nejvetsi baleni k vypoctu
reset($baleni);
if ($muzuvyhazovat) $n+=current($baleni);
/* ceny je pole, kde budu ukladat navrhy kombinaci pro jednotlive pocty.
* klic bude pocet, hodnoty budou mit nasledujici strukturu
* cena .. celkova cena
* index .. prvni index ze ktereho se sklada. Pokud jde o baleni, nebude existovat
* viz nasledny vypocet. Druhy index je pocet-index. Napr. 42 s indexem 15 znamena ze
* se sklada z vypoctenych kombinaci baleni 15 a (42-15) tj. 27
* pokud muzu vyhazovat, a bude index zaporny, znamena to ze mam koupit klic - index a zbytek vyhodit
*/
$ceny=array();
// baleni 1ks je nutno vzit z definice.
if (isset($baleni[1])) {
$ceny[1]["cena"]=$baleni[1];
} else {
$ceny[1]["cena"]=$nejmensibalenicena*2;
}
// vyplnim hodnoty pro vsechny ostatni pocty az po n
for ($i=2;$i<=$n;$i++) {
// kazde baleni lze vyjadrit jako kombinaci dvou jinych baleni o mensich mnozstvich
// je jasne ze kombinaci je mnozstvi/2. A take je jasne ze jedna z techto kombinaci je nejlepsi
// jako vychozi nejlepsi kombinaci ulozim soucet baleni 1 a o 1 mensi nez hledane
$nejindex=1;
$nejcena=$ceny[1]["cena"]+$ceny[$i-1]["cena"];
// staci prohledavat do poloviny hodnoty
for ($j=2;$j<intval($i/2);$j++) {
if ($nejcena>$ceny[$j]["cena"]+$ceny[$i-$j]["cena"]) {
$nejcena=$ceny[$j]["cena"]+$ceny[$i-$j]["cena"];
$nejindex=$j;
}
}
// ted zjistim jestli nahodou neni baleni o hledanem mnozstvi a nema lepsi cenu nez nalezena kombinace
// pozor musim dat na prepocet celkove ceny a ceny za kus
if (isset($baleni[$i]) and ($baleni[$i]*$i<$nejcena)) {
// mam baleni a vychazi lepe
$ceny[$i]["cena"]=$baleni[$i]*$i;
} else {
// ne, je potreba to poskladat
$ceny[$i]["cena"]=$nejcena;
$ceny[$i]["index"]=$nejindex;
}
}
// v pripade ze mam povoleno vyhazovat, zjistim jestli je vyssi cena pro nizsi index. Pokud ano, tak nastavuju zaporny index
if ($muzuvyhazovat) {
for ($i=$n-1;$i>=1;$i--)
if ($ceny[$i+1]["cena"]<=$ceny[$i]["cena"]) {
// vyplati se vyhodit, vyhazuju i v pripade ze je cena stejna
if (!isset($ceny[$i+1]["index"]) or ($ceny[$i+1]["index"]>0)) {
// je to cele baleni, napr. 50ks, nebo kladny index v baleni
$ceny[$i]["index"]=-1;
} else {
// uz o 1 vyssi pocet je lepe vyhodit, takze index dam rovnou na to puvodni
// napr zkoumam 48. 49 melo index -1 a ukazovalo na 50. Spoctena cena je porad vyssi nez pro 50, index dam -2 (=-1-1)
$ceny[$i]["index"]=$ceny[$i+1]["index"]-1;
}
// a dosadim novou cenu
$ceny[$i]["cena"]=$ceny[$i-$ceny[$i]["index"]]["cena"];
}
}
// ted musim udelat vysledky
// do pole objednat naskladam vsechny indexy baleni, hodnota bude pocet baleni
// nastavim na 0
$objednat=array();
foreach ($baleni as $pocet=>$tmp) $objednat[$pocet]=0;
// na naplneni si udelam rekurentni funkci (zabere to mene radku)
// misto global to muze byt predavano odkazem, tohle je hezci
function pridej($mnozstvi) {
global $objednat,$ceny;
if (!isset($ceny[$mnozstvi]["index"])) {
// je to cele baleni
$objednat[$mnozstvi]++;
} else {
pridej($mnozstvi-$ceny[$mnozstvi]["index"]);
if ($ceny[$mnozstvi]["index"]>0) {
// u slozeneho bez vyhazovani i druhou cast
pridej($ceny[$mnozstvi]["index"]);
}
}
}
// pokud je ks>nsnb tak nejprve pridam nsnb nasobene poctem nsnb v ks
$x=$ks;
if ($x>$nsnb) {
pridej($nsnb);
$nasob=intval($ks/$nsnb);
foreach ($objednat as $pocet=>$tmp) $objednat[$pocet]*=$nasob;
$x-=$nasob*$nsnb;
}
pridej($x);
// uz jen ukazat vysledky
$celkem=0;
echo "Objednavka:<hr>\n";
foreach ($objednat as $pocet=>$chceme)
if ($chceme) {
echo "$chceme x baleni $pocet ks<br>\n";
$celkem+=$baleni[$pocet]*$pocet*$chceme;
}
echo "<hr>CELEKEM $celkem penez<br>\n";
?>