Masalah Arung Jeram (varian Knapsack)

20

Teka-teki pertama dari saya, saran untuk perbaikan diterima dengan senang hati!

Skenarionya adalah; Anda bekerja sebagai manajer untuk perusahaan arung jeram. Setiap pagi, Anda diberikan daftar pemesanan, dan Anda harus mengurutkannya menjadi muatan rakit. Tulis program atau fungsi dalam bahasa pilihan Anda yang melakukan ini untuk Anda.

Setiap rakit menampung maksimal nklien, dan setiap pemesanan adalah untuk grup antara 1 dan norang (termasuk). Aturan berikut harus diperhatikan;

  • Tidak ada grup yang dapat dipisahkan. Jika mereka memesan bersama, mereka semua harus berada di rakit yang sama.

  • Jumlah rakit harus diminimalkan.

  • Tunduk pada dua aturan sebelumnya, kelompok-kelompok tersebut harus disebarkan sepadan mungkin di antara rakit.

Input Jumlahnya n(Anda dapat berasumsi bahwa ini adalah bilangan bulat positif), dan ukuran semua pemesanan. Ini bisa berupa susunan, daftar atau struktur data serupa jika bahasa Anda mendukung hal-hal seperti itu. Semua ini akan menjadi bilangan bulat positif antara 1 dan n. Urutan pemesanan tidak ditentukan, juga tidak penting.

Keluaran. Daftar nomor pemesanan, dikelompokkan ke dalam muatan rakit. Pengelompokan harus ditunjukkan dengan jelas, seperti;

  • daftar, atau array array.
  • daftar yang dipisahkan koma untuk setiap rakit. Baris baru di antara setiap rakit.

Bagaimana Anda menerapkan aturan ketiga itu terserah Anda, tetapi ini bisa melibatkan menemukan hunian rakit rata-rata, dan meminimalkan penyimpangan dari itu sebanyak mungkin. Berikut ini beberapa test case.

n  Bookings       Output
6  [2,5]          [5],[2]
4  [1,1,1,1,1]    [1,1,1],[1,1]
6  [2,3,2]        [2,2],[3]
6  [2,3,2,3]      [2,3],[2,3]
6  [2,3,2,3,2]    [2,2,2],[3,3]
12 [10,8,6,4,2]   [10],[8,2],[6,4]
6  [4,4,4]        [4],[4],[4]
12 [12,7,6,6]     [12],[7],[6,6]

Aturan standar berlaku, kode terpendek menang. Selamat bersenang-senang!

Diedit; Cara yang disarankan untuk mendefinisikan sama sederajat mungkin untuk aturan ketiga.

Setelah jumlah rakit rtelah ditentukan (tunduk pada aturan kedua), hunian rata-rata adapat dihitung dengan menjumlahkan pemesanan, dan membaginya dengan r. Untuk setiap rakit, penyimpangan dari hunian rata-rata dapat ditemukan menggunakan d(x) = abs(n(x)-a), di mana n(x)jumlah orang di setiap rakit dan 1 <= x <= r. Untuk beberapa fungsi kontinu, bernilai-tunggal f(y), yang benar-benar positif dan memiliki turunan kedua yang benar-benar positif pertama dan non-negatif untuk semua positif y, kami mendefinisikan kuantitas non-negatif F, sebagai jumlah dari semua f(d(x)), 1 <= x <= r. Setiap pilihan alokasi rakit yang memenuhi dua aturan pertama, dan di mana Fsama dengan minimum global akan memenuhi aturan ketiga juga.

Gwyn
sumber
3
Untuk referensi di masa mendatang Anda dapat memposting ke kotak pasir kami untuk mendapatkan umpan balik tentang tantangan sebelum Anda memposting.
Wheat Wizard
Selamat Datang di Programming Puzzles & Code Golf! Ini terlihat seperti tantangan yang bagus, mengetahui bahwa ini adalah tantangan pertama Anda. Namun, lain kali, mungkin lebih baik mengirimkan tantangan di Sandbox terlebih dahulu, sehingga orang dapat memberikan saran di sana. Kemudian ketika Anda berpikir tantangan sudah selesai, Anda dapat mempostingnya di situs utama. Terima kasih telah membaca, dan semoga harimu menyenangkan!
Matthew Roh
Bagaimana sedapat mungkin diukur?
Dennis
@Dennis; Saya akan memberikan cara yang disarankan untuk mendefinisikan ini dalam pengeditan. Namun jika Anda memiliki metode yang berbeda, dan dapat membenarkannya untuk jawaban Anda, itu tidak masalah.
Gwyn
1
Membiarkan hal-hal hingga implementasi baik-baik saja, selama itu jelas apa yang valid dan apa yang tidak, dan hasil edit terbaru Anda mencapai imo itu. Saya agak menduga bahwa kita tidak dapat menggunakan g(y) = y(kedua turunan nol) atau g(y) = y²(pertama menurunkan nol ketika y = 0).
Dennis

Jawaban:

2

Perl 6 , 163 158 byte

{[grep $^n>=*.all.sum,map ->\p{|map {p[0,|$_ Z..^|$_,p]},(1..^p).combinations},$^s.permutations].&{.grep: .map(+*).min}.min({.map((*.sum-$s.sum/$_)**2).sum})}

Cobalah online!

Bagaimana itu bekerja

  • map ->\p{|map {p[0,|$_ Z..^|$_,p]},(1..^p).combinations},$^s.permutations

    Menghasilkan semua kemungkinan partisi dari semua permutasi dari input array.

  • grep $^n>=*.all.sum,

    Memfilter yang tidak memiliki rakit berlebih.

  • .&{.grep: .map(+*).min}

    Memfilter yang jumlah rakitnya minimal.

  • .min({.map((*.sum-$s.sum/$_)**2).sum})}

    Mendapat yang pertama dengan minimal ¢ (n x -a) 2 .

-4 byte terima kasih kepada @ Pietu1998

seseorang
sumber
Apakah yang perlu Anda lakukan .absjika hasilnya sesuai?
PurkkaKoodari
@ Pietu1998: Saya tidak tahu, tangkapan bagus.
smls
3

Haskell 226 228 234 268 byte

Jawaban naif di Haskell

import Data.List
o=map
u=sum
p=foldr(\x t->o([x]:)t++[(x:y):r|(y:r)<-t>>=permutations])[[]]
m x=foldl(\[m,n]x->[m+(x-m)/(n+1),n+1])[0,0]x!!0
a!z=abs$u z-a
s t=(length t,u$o((m$o u t)!)t)
a n=head.sortOn s.filter(all$(<=n).u).p

Atau ungolfed

partition' :: [a] -> [[[a]]]
partition' [] = [[]]
partition' (x:xs) = [[x]:ps     | ps <- partition' xs]
                 ++ [(x:p):rest | ps <- partition' xs, (p:rest) <- permutations ps]

-- from Data.Statistics
mean :: [Double] -> Double
mean xs = fst $ foldl (\(m, n) x -> (m+(x-m)/n+1, n+1)) (0, 0) xs

diff :: Double -> [Double] -> Double
diff avg xs = abs $ sum xs - avg

rawScore :: [[Double]] -> Double
rawScore xs = sum . map (diff avg) $ xs where avg = mean . map sum $ xs

score :: [[Double]] -> (Int, Double)
score xs = (length xs, rawScore xs)

-- from Data.Ord
comparing :: (Ord b) => (a -> b) -> a -> a -> Ordering
comparing p x y = compare (p x) (p y)

candidates :: Double -> [Double] -> [[[Double]]]
candidates n xs = filter (all (\ ys -> sum ys <= n)) . partition' $ xs

answer :: Double -> [Double] -> [[Double]]
answer n xs = minimumBy (comparing score) $ candidates n xs

Dengan beberapa test case

import Text.PrettyPrint.Boxes

testCases :: [(Double, [Double])]
testCases = [(6 , [2,5])
            ,(4 , [1,1,1,1,1])
            ,(6 , [2,3,2])
            ,(6 , [2,3,2,3])
            ,(6 , [2,3,2,3,2])
            ,(12, [10,8,6,4,2])
            ,(6 , [4,4,4])
            ,(12, [12,7,6,6])]

runTests tests = transpose 
                 $ ["n", "Bookings", "Output"]
                 : map (\(n, t) -> [ show . floor $ n
                                   , show . map floor $ t
                                   , show . map (map floor) $ a n t]) tests

test = printBox 
     . hsep 3 left . map (vcat top) . map (map text) . runTests $ testCases

Di mana testhasil

n    Bookings       Output
6    [2,5]          [[2],[5]]
4    [1,1,1,1]      [[1,1],[1,1,1]]
6    [2,3,2]        [[2,2],[3]]
6    [2,3,2,3]      [[2,3],[2,3]]
6    [2,3,2,3,2]    [[2,2,2],[3,3]]
12   [10,8,6,4,2]   [[10],[8,2],[6,4]]
6    [4,4,4]        [[4],[4],[4]]
12   [12,7,6,6]     [[12],[7],[6,6]]

Edit

Terima kasih kepada @ flawr dan @nimi untuk sarannya.

Tergencet p sedikit.

Memotong beberapa byte.

walpen
sumber
1
Anda bisa mengatur s=sumdan kemudian menggunakan sbukannya sum, dan mungkin Anda juga bisa mengganti fst$ ...dengan ...!!0.
flawr
1
Anda dapat mengganti minimumBy(c s)dengan head.sortOn sdan menghapus fungsi c. Juga: \t->sum t<=nadalah (<=n).sum.
nimi
@ flawr, saran bagus, terima kasih!
walpen
0

Python3, 224 byte

def p(c):
 if len(c)==1:yield[c];return
 for s in p(c[1:]):
  for n,u in enumerate(s):yield s[:n]+[[c[0]]+u]+s[n+1:]
  yield[[c[0]]]+s
s=sum
r=lambda n,b:min(p(b),key=lambda c:s(abs(s(x)-s(b)/(s(b)//n+1))for x in c))

Dengan testcases:

tc = [[6,[2,5]],[4,[1,1,1,1,1]],[6,[2,3,2]],[6,[2,3,2,3]],[6,[2,3,2,3,2]],[12,[10,8,6,4,2]],[6,[4,4,4]],[12,[12,7,6,6]]]
for case in tc:
    print(str(case[0]).ljust(3),str(case[1]).ljust(16),"|",r(*case))

Bagaimana cara kerjanya?

The pFungsi hanya menghasilkan semua partisi dari daftar yang diberikan (semua cara yang mungkin untuk membaginya menjadi sublists). s=sumcukup mengganti nama fungsi penjumlahan, sehingga baris terakhir melakukan semua pekerjaan.

r=lambda n,b:min(p(b),key=lambda c:s(abs(s(x)-s(b)/(s(b)//n+1))for x in c))
r=lambda n,b:                                                               Initialize the lambda
                 p(b)                                                       Calculate all possible raft arrangements
                     ,key=lambda c:                                         Map the following lambda onto the list:
                                              s(b)/(s(b)//n+1)              Calculate the ideal average amount of people per raft
                                     abs(s(x)-                )             Calculate how close is the current raft
                                                               for x in c   For each raft in the partition
                                   s(                                    )  Sum it (the sum is a score of how close to ideal the function is),
             min(                                                         ) And find the lowest valued partition.

Saya yakin ini bisa di-golf lebih jauh, terutama pfungsinya, tapi saya sudah mengerjakan ini selama berjam-jam, jadi begini.

sagiksp
sumber