Մետաղադրամների փոխարկման խնդիրը (Coin change) Դինամիկ Ծրագրավորման (Dynamic Programming) ամենահայտնի օրինակներից է. այն ցույց է տալիս, թե ինչպես Ագահ Ալգորիթմ (Greedy algorithm) կիրառելու դեպքում կարելի է ստանալ ենթաօպտիմալ պատասխան նրանց փոխարեն, որ անհրաժեշտ է լուծել խնդիրը գլոբալ մակարդակով:
Տրված է դրամի համակարգ, որը կազմված է n մետաղադրամներից, որտեղ յուրաքանչյուր մետաղադրամի արժեքը դրական է ։ Պահանջվում է ստանալ x գումարը հնարավորինս քիչ մետաղադրամների միջոցով:
Օրինակ, եթե մետաղադրամների արժեքներն են և անհրաժեշտ է ստանալ 11, լավագույն տարբերակը կլինի վերցնել 1 + 5 + 5 ⇒ 3 մետաղադրամ:
Մուտք
Մուտքի առաջին տողում տրված է երկու ամբողջ թիվ n (1 ≤ n ≤ 100) և x (1 ≤ x ≤ )։
Հաջորդ տողում գրված են n ամբողջ թվեր (1 ≤ ≤ )։
Ելք
Ծրագիրը պետք է արտահանի նվազագույն մետաղադրամների քանակը, որոնք անհրաժեշտ են x գումարը ստանալու համար։ Եթե հնարավոր չէ ստանալ x գումարը, պետք է տպել -1։
Օրինակներ
Մուտք
Ելք
3 11
1 5 7
3
3 12
1 5 7
2
Բացատրություն
11 → 1 + 5 + 5
12 → 5 + 7
Ուղեցույց
Զգուշացեք, որ ամենամեծ արժեքով մետաղադրամը մշտապես վերցնելը (ագահ մոտեցումը) չի գործի. առաջին օրինակում դա հստակ երևում է: Եթե վերցնենք 7, ապա մնում է 4, որը կարող ենք ստանալ 4 հատ 1-ով. արդյունքում կօգտագործենք 5 մետաղադրամ, մինչդեռ իրական պատասխանը 3-ն է (5 + 5 + 1):
Եկեք սահմանենք d[i] որպես նվազագույն մետաղադրամների քանակը, որոնք անհրաժեշտ են i գումարը ստանալու համար: Եթե կարողանանք ճիշտ սկզբնական արժեքներ տալ d զանգվածին և ապա համալրել այն մինչև x, ապա պատասխանը պարզապես կլինի d[x]:
Սկզբում կարող ենք դնել d-ի բոլոր անդամները հավասար ∞ (նշանակում է՝ տվյալ գումարը ստանալն անհնար է), բացի 0-ից, որը հնարավոր է ստանալ 0 մետաղադրամով.
c = [...]
x = ...
d = [0] + [float('inf')] * x # d[0] = 0 և d[1...x] ներառյալ ∞
Այնուհետև, 1-ից մինչև x յուրաքանչյուր թվի համար պետք է փորձենք գտնել, թե ինչքան մետաղադրամ է հարկավոր: Եթե արդեն հասել ենք ինչ-որ թվի num, ապա ստուգում ենք բոլոր հնարավոր մետաղադրամները՝ արդյոք հնարավոր է ստանալ num արժեքը num - -ից +1 մետաղադրամով: Եթե դա փոքրացնում է հաշվարկը, թարմացնում ենք d[num]:
for num in range(1, x + 1): # Շրջանցում ենք 1...x արժեքները
for ci in c: # Փորձում ենք բարելավել d[num]-ը բոլոր մետաղադրամներով
if num - ci >= 0: # Եթե կարելի է ստանալ num արժեքը ci մետաղադրամով
d[num] = min(d[num], d[num - ci] + 1)
print(d[x])
d[num] = min(d[num], d[num - ci] + 1) տողը խնդրի լուծման առանցքային քայլն է. ամեն փուլում փորձում ենք բարելավել d[num]-ը այն տարբերակով, որ num - ci արժեքը հասանելի է ամենաքիչ մետաղադրամով, որին գումարում ենք 1 (մեր օգտագործվող ci մետաղադրամը):
Երբ բոլոր արժեքները հաշվել ենք, վերջնական պատասխանն ուղղակի d[x] արժեքն է:
Եկեք քայլ առ քայլ դիտարկենք ալգորիթմը տվյալตัว օրինակի վրա.
c = [1, 5, 7] և x = 11
d = [0, ∞, ∞, ∞, ∞, ∞, ∞, ∞, ∞, ∞, ∞, ∞]
num = 1
ci = 1 ⇒ d[1] = d[0] + 1 = 1 ⇒ d = [0, 1, ∞, ∞, ∞, ∞, ∞, ∞, ∞, ∞, ∞, ∞]
ci = 5 ⇒ num - ci < 0
ci = 7 ⇒ num - ci < 0
num = 2
ci = 1 ⇒ d[2] = d[1] + 1 = 2 ⇒ d = [0, 1, 2, ∞, ∞, ∞, ∞, ∞, ∞, ∞, ∞, ∞]
ci = 5 ⇒ num - ci < 0
ci = 7 ⇒ num - ci < 0
num = 3
ci = 1 ⇒ d[3] = d[2] + 1 = 3 ⇒ d = [0, 1, 2, 3, ∞, ∞, ∞, ∞, ∞, ∞, ∞, ∞]
ci = 5 ⇒ num - ci < 0
ci = 7 ⇒ num - ci < 0
num = 4
ci = 1 ⇒ d[4] = d[3] + 1 = 4 ⇒ d = [0, 1, 2, 3, 4, ∞, ∞, ∞, ∞, ∞, ∞, ∞]
ci = 5 ⇒ num - ci < 0
ci = 7 ⇒ num - ci < 0
num = 5
ci = 1 ⇒ d[5] = d[4] + 1 = 5 ⇒ d = [0, 1, 2, 3, 4, 5, ∞, ∞, ∞, ∞, ∞, ∞]
ci = 5 ⇒ d[5] = d[0] + 1 = 1 ⇒ d = [0, 1, 2, 3, 4, 1, ∞, ∞, ∞, ∞, ∞, ∞]
ci = 7 ⇒ num - ci < 0
num = 6
ci = 1 ⇒ d[6] = d[5] + 1 = 2 ⇒ d = [0, 1, 2, 3, 4, 1, 2, ∞, ∞, ∞, ∞, ∞]
ci = 5 ⇒ does not update
ci = 7 ⇒ num - ci < 0
num = 7
ci = 1 ⇒ d[7] = d[6] + 1 = 3 ⇒ d = [0, 1, 2, 3, 4, 1, 2, 3, ∞, ∞, ∞, ∞]
ci = 5 ⇒ does not update
ci = 7 ⇒ d[7] = d[0] + 1 = 1 ⇒ d = [0, 1, 2, 3, 4, 1, 2, 1, ∞, ∞, ∞, ∞]
num = 8
ci = 1 ⇒ d[8] = d[7] + 1 = 2 ⇒ d = [0, 1, 2, 3, 4, 1, 2, 1, 2, ∞, ∞, ∞]
ci = 5 ⇒ does not update
ci = 7 ⇒ does not update
num = 9
ci = 1 ⇒ d[9] = d[8] + 1 = 3 ⇒ d = [0, 1, 2, 3, 4, 1, 2, 1, 2, 3, ∞, ∞]
ci = 5 ⇒ does not update
ci = 7 ⇒ does not update
num = 10
ci = 1 ⇒ d[10] = d[9] + 1 = 4 ⇒ d = [0, 1, 2, 3, 4, 1, 2, 1, 2, 3, 4, ∞]
ci = 5 ⇒ d[10] = d[5] + 1 = 2 ⇒ d = [0, 1, 2, 3, 4, 1, 2, 1, 2, 3, 2, ∞]
ci = 7 ⇒ does not update
num = 11
ci = 1 ⇒ d[11] = d[10] + 1 = 3 ⇒ d = [0, 1, 2, 3, 4, 1, 2, 1, 2, 3, 2, 3]
ci = 5 ⇒ does not update
ci = 7 ⇒ does not update