Роздільність долара та ідеальна зміна


11

У мене в кишені 15 доларів. Так само я перебуваю в магазині, який не дає змін. Під час перегляду я помічаю товар, який коштує 10 доларів США (з податком). Чи можу я придбати цей предмет, не втрачаючи грошей?

У цьому випадку відповідь - так. Незалежно від того, як розділено мої 15 доларів (один 10 і один 5, або три 5, або щось інше), у мене завжди будуть потрібні точні 10 доларів.

Як другий приклад, у мене в кишені 0,16 долара. Які ще гроші я повинен мати можливість платити саме?

Possible Divisions:
0.01, 0.05, 0.10
0.01, 0.05 x 3
0.01 x 16
Guaranteed Exact Change:
0.01, 0.05, 0.06, 0.10, 0.11, 0.15, 0.16

Що робити, якщо у мене в кишені є 0,27 долара?

Possible Divisions:
0.01 x 2, 0.25
0.01 x 2, 0.05, 0.10 x 2
0.01 x 2, 0.05 x 3, 0.10
0.01 x 2, 0.05 x 5
0.01 x 27
Guaranteed Exact Change:
0.01, 0.02, 0.25, 0.26, 0.27

У вищенаведеному випадку було лише кілька грошей, на які я завжди мав би ідеальну зміну.

Ваше завдання

Напишіть найкоротшу програму (або названу функцію), яка приймає A) цілу кількість грошей і B) список можливих номіналів як вхідних даних, і виводить список сум грошей, на які я повинен мати ідеальні зміни. Вхід може бути STDIN або аргументом для програми або функції. Я не буду надто суворим щодо форматування вводу; це може відповідати тому, як ваші мови форматують масиви.

Можливо, більш детальне пояснення

У мене в кишені є певна кількість грошей, яка формується з набору можливих демонстрацій валюти. Якщо у мене є 8 доларів, і я знаю, що можливі номінали - 2 і 3 долари, то є лише стільки різних комбінацій купюр, які можуть бути в моїй кишені. Це є 2+2+2+2і 3+3+2. Щоб я міг отримати точну суму грошей, я маю змогу виготовити цю кількість, використовуючи лише ті рахунки, які є в моїй кишені. Якби у мене було чотири 2, я б міг виробляти 2, 4, 6, or 8. Якби у мене було два 3-х та 2-х, я міг би створити, 2, 3, 5, 6, or 8оскільки я не знаю, яку з цих комбінацій насправді маю в кишені, моя остаточна відповідь зводиться до 2, 6, 8. Це ті значення, які я знаю, що я міг би отримати з кишені, враховуючи загальну суму та можливі номінали.

Приклад введення / виводу вручну

7 [3, 4]
3, 4, 7        //only one possible division into 3 + 4

7 [3, 2]
2, 3, 4, 5, 7  //the only division is 3 + 2 + 2

6 [2, 3, 4]
6     //divisions are 2+2+2, 3+3, 2+4 

16 [1, 5, 10, 25]          //this represents one of the examples above
1, 5, 6, 10, 11, 15, 16

27 [1, 5, 10, 25]          //another example from above
1, 2, 25, 26, 27

1500 [1, 5, 10, 25, 100, 500, 1000, 2000]
500, 1000, 1500

600 [100, 500, 1000, 2000]
100, 500, 600

600 [200, 1, 5, 10, 25, 100, 500, 1000, 2000]
600

Це дуже незрозуміло.
мотоку

@FryAmTheEggman Я додав "можливо більш детальне пояснення". Повідомте мене, якщо це все ще заплутано. (Я також зняв крайовий корпус, тому що це було майже безглуздо.)
PhiNotPi

Я не бачу, як ти отримуєш 6 [2, 3, 4]. Не можете 2+2+2зробити 3 і 3+3не зробити 2 і 4?
xnor

@xnor ви правильні, виправлені.
PhiNotPi

Чи повинна програма працювати в розумний час для всіх вхідних даних? Наприклад, моя найкоротша ідея є експоненціальною в початковій сумі, а 2 ^ 1500 - це багато чого.
randomra

Відповіді:


2

Python 2, 200 197 193 140 байт

f=lambda n,D,S={0}:sum([f(n-x,D,S|{x+y for y in S})for x in D],[])if n>0else[S]*-~n
g=lambda*a:(f(*a)and reduce(set.__and__,f(*a))or{0})-{0}

(Дякую @Nabb за поради)

Ось зараз погано розроблене рішення для гольфу, щоб почати все. Call with g(16, [1, 5, 10, 25])- output - це набір із відповідними номіналами.

Підхід простий і розбивається на два етапи:

  • fрозглядає всі способи досягнення nноміналів D(наприклад [1, 5, 10]), і для кожної з них розроблені всі суми, які можна зробити за допомогою цих номіналів (наприклад set([0, 1, 5, 6, 10, 11, 15, 16])).
  • gобчислює перетин результатів f, а потім видаляє 0 для остаточної відповіді.

Програма вирішує випадки 1-5 та 7 штрафу, стек переповнює 6 та займає назавжди 8.

Якщо рішення немає (наприклад g(7, [2, 4, 6])), програма повертає порожній набір. Якщо для такої справи допущено помилку, то тут коротше g:

g=lambda*a:reduce(set.__and__,f(*a))-{0}

g=lambda L,c=0:L and g(L[1:],c)|g(L,c+L.pop(0))or{c}трохи коротше
Nabb

Трохи більше, перейшовши -{0}на g і використовуючи [L]*-~nзамість[L][-n:]
Nabb

1

JavaScript (ES6) 162 203 207

Редагування Змінено спосіб перетину наборів результатів у масиві r. Трохи швидше, але алгоритм все одно смердить.

Більш детальне пояснення буде далі.
Коротко: c - рекурсивна функція, яка перераховує всі можливі підрозділи. k - рекурсивна функція, яка перераховує всі можливі суми без повторів. Будь-який новий набір результатів, знайдений з функцією k, порівнюється з попереднім знайденим набором, зберігаються лише загальні результати.

Чому так повільно? Необхідно керувати цільовою загальною сумою, скажімо, 1500 та однією частиною вартості 1, перераховувати всі можливі суми не є хорошою ідеєю.

F=(s,d,r,
  c=(s,i,t=[],v,k=(i,s,v)=>{for(;v=t[i++];)k(i,s+v);o[s]=s})=>
  {for(s||(i=k(o=[],0),r=(r||o).filter(v=>o[v]));v=d[i];++i)s<v||c(s-v,i,[...t,v])}
)=>c(s,0)||r

Безумовно

F=(s,d)=>{
  var r
  var c=(s,i,t=[])=>
  {
    var o=[],v
    var k=(i,s)=> // find all sums for the current list t, set a flag in the o array
    {
      var v
      for(;v=t[i++];)k(i,s+v)
      o[s]=s
    }

    if (s==0) {
      k(0,0)
      if (r)
        r = r.filter(v=>o[v]) // after first loop, intersect with current
      else
        r = o.filter(v=>v) // first loop, keep all results
    } 
    else
      for(;v=d[i];++i)
      { 
        if (s >= v) 
          c(s-v, i, t.concat(v))
      }
  }
  c(s,0) // enumerate all possible set of pieces
  return r
}

Тест у консолі Firefox / FireBug

F(16,[1,5,10,25])

[1, 5, 6, 10, 11, 15, 16]

(час 84 мсек)

F(27, [1, 5, 10, 25]) 

[1, 2, 25, 26, 27]

(час 147252 мс, тому не так швидко)


0

Wolfram Methematica, 104 байти

Rest@*Intersection@@Map[Total]/@Subsets/@Union[Sort/@IntegerPartitions[#,#,PadLeft[{},Length[#2]#,#2]]]&

Безумовно (читати з кінця):

Rest@* // Removing 0
  Intersection@@   // Intersecting all totals
     Map[Total]/@  // Counting total of each subset
        Subsets/@  // Getting all the subsets of each partition
           Union[  // Removing duplicates 
              Sort/@ // Sorting each partition (to remove duplicates next)
                 IntegerPartitions[#,#,PadLeft[{},Length[#2]#,#2]] // Getting all Integer partitions
                ]&
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.