Divisibilitas Dolar dan Perubahan Sempurna

11

Saya memiliki $ 15 di saku saya. Demikian juga, saya berada di toko yang tidak memberikan perubahan. Saat menjelajah, saya melihat item yang harganya $ 10 (termasuk pajak). Bisakah saya membeli barang itu tanpa kehilangan uang?

Dalam hal ini, jawabannya adalah ya. Tidak peduli bagaimana $ 15 saya dibagi (satu 10 dan satu 5, atau tiga 5s, atau yang lain), saya akan selalu memiliki $ 10 persis yang dibutuhkan.

Sebagai contoh kedua, saya memiliki $ 0,16 di saku saya. Berapa jumlah uang lain yang harus saya bayarkan?

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

Bagaimana jika saya memiliki $ 0,27 di saku saya?

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

Dalam kasus di atas, hanya ada beberapa jumlah uang yang saya akan selalu memiliki perubahan sempurna.

Tugas Anda

Tuliskan program terpendek (atau fungsi bernama) yang mengambil A) jumlah uang integer dan B) daftar kemungkinan denominasi sebagai input, dan mengeluarkan daftar jumlah uang yang saya harus memiliki perubahan sempurna. Input dapat berupa STDIN atau argumen untuk program atau fungsi. Saya tidak akan menjadi super ketat pada pemformatan input; itu bisa cocok dengan bagaimana format bahasa Anda array.

Mungkin Penjelasan Lebih Detail

Saya memiliki sejumlah uang di saku, yang terbentuk dari serangkaian kemungkinan demonstrasi mata uang. Jika saya memiliki $ 8, dan saya tahu bahwa denominasi yang mungkin adalah $ 2 dan $ 3, maka hanya ada begitu banyak kombinasi tagihan yang berbeda yang ada di saku saya. Ini 2+2+2+2dan 3+3+2. Agar dapat menghasilkan jumlah uang yang tepat, saya harus dapat menghasilkan jumlah itu dengan hanya menggunakan tagihan yang ada di saku saya. Jika saya punya empat 2, saya bisa menghasilkan 2, 4, 6, or 8. Jika saya memiliki dua angka 3 dan 2, saya bisa menghasilkan. 2, 3, 5, 6, or 8Karena saya tidak tahu kombinasi mana yang sebenarnya saya miliki di saku saya, jawaban akhir saya berkurang menjadi 2, 6, 8. Inilah nilai-nilai yang saya tahu dapat saya hasilkan dari saku, mengingat jumlah total dan kemungkinan denominasi.

Contoh Tangan-Dihitung I / O

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
PhiNotPi
sumber
Ini sangat tidak jelas.
motoku
@FryAmTheEggman Saya telah menambahkan "mungkin penjelasan yang lebih rinci." Beritahu saya jika masih membingungkan. (Saya juga melepas
kasing
Saya tidak melihat bagaimana Anda mendapatkan 6 [2, 3, 4]. Tidak dapat 2+2+2membuat 3, dan 3+3tidak membuat 2 dan 4?
xnor
@xnatau Anda benar, sudah diperbaiki.
PhiNotPi
Haruskah program berjalan pada waktu yang wajar untuk semua input? Misalnya ide saya yang terpendek adalah eksponensial dalam jumlah awal dan 2 ^ 1500 adalah banyak hal.
randomra

Jawaban:

2

Python 2, 200 197 193 140 byte

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}

(Terima kasih kepada @Nabb untuk kiatnya)

Berikut ini adalah solusi golf yang buruk untuk saat ini untuk memulai sesuatu. Panggilan dengan g(16, [1, 5, 10, 25])- keluaran adalah seperangkat denominasi yang relevan.

Pendekatannya mudah, dan dipecah menjadi dua langkah:

  • fmelihat semua cara untuk mencapai ndengan denominasi D(mis. [1, 5, 10]), dan untuk masing-masing itu berhasil semua jumlah yang dapat dibuat dengan denominasi ini (misalnya set([0, 1, 5, 6, 10, 11, 15, 16])).
  • gmenghitung persimpangan hasil f, lalu menghilangkan 0 untuk jawaban akhir.

Program ini menyelesaikan case 1-5 dan 7 fine, menumpuk overflow 6 dan berlangsung selamanya 8.

Jika tidak ada solusi (mis. g(7, [2, 4, 6])), Maka program mengembalikan set kosong. Jika kesalahan diizinkan untuk kasus seperti itu, maka inilah yang lebih pendek g:

g=lambda*a:reduce(set.__and__,f(*a))-{0}
Sp3000
sumber
g=lambda L,c=0:L and g(L[1:],c)|g(L,c+L.pop(0))or{c}sedikit lebih pendek
Nabb
Sedikit lebih banyak dengan pindah -{0}ke g dan menggunakan [L]*-~nbukannya[L][-n:]
Nabb
1

JavaScript (ES6) 162 203 207

Sunting Mengubah cara untuk memotong set hasil dalam array r. Sedikit lebih cepat, tetapi algoritmanya masih bau.

Penjelasan lebih rinci akan mengikuti.
Singkatnya: c adalah fungsi rekursif yang menyebutkan semua subdivisi yang mungkin. k adalah fungsi rekursif yang menghitung semua jumlah yang mungkin tanpa pengulangan. Setiap set hasil baru yang ditemukan dengan fungsi k dibandingkan dengan set sebelumnya yang ditemukan, hanya hasil umum yang disimpan.

Kenapa sangat lambat? Harus mengelola total target, katakanlah, 1500 dan sepotong nilai 1, menghitung semua jumlah yang mungkin bukan ide yang baik.

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

Tidak disatukan

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
}

Uji di Firefox / konsol FireBug

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

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

(waktu 84 msec)

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

[1, 2, 25, 26, 27]

(waktu 147252 msec, jadi tidak terlalu cepat)

edc65
sumber
0

Wolfram Methematica, 104 byte

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

Tidak Digubah (baca dari akhir):

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
                ]&
Savenkov Alexey
sumber