Vấn đề đi bè (biến thể Knapsack)


20

Câu đố đầu tiên từ tôi, đề nghị cải thiện sẵn sàng nhận được!

Kịch bản là; Bạn làm quản lý cho một công ty đi bè nước trắng. Mỗi buổi sáng, bạn được cung cấp một danh sách các đặt phòng, và bạn phải sắp xếp chúng thành tải bè. Viết chương trình hoặc chức năng bằng ngôn ngữ bạn chọn thực hiện điều này cho bạn.

Mỗi bè chứa tối đa nkhách hàng và mỗi đặt phòng dành cho một nhóm từ 1 đến nngười (bao gồm). Các quy tắc sau đây phải được tuân thủ;

  • Không có nhóm có thể được tách ra. Nếu họ đặt cùng nhau, tất cả họ phải ở trong cùng một chiếc bè.

  • Số lượng bè phải được giảm thiểu.

  • Theo hai quy tắc đặt trước, các nhóm phải được trải đều như nhau giữa các bè.

Đầu vào. Số n(bạn có thể cho rằng đây là số nguyên dương) và kích thước của tất cả các đặt phòng. Đây có thể là một mảng, danh sách hoặc cấu trúc dữ liệu tương tự nếu ngôn ngữ của bạn hỗ trợ những thứ đó. Tất cả những thứ này sẽ là số nguyên dương giữa 1 và n. Thứ tự của các đặt phòng không được xác định, cũng không quan trọng.

Đầu ra. Một danh sách các số đặt phòng, được nhóm thành tải bè. Việc nhóm phải được chỉ định rõ ràng, chẳng hạn như;

  • một danh sách, hoặc mảng của mảng.
  • một danh sách được phân tách bằng dấu phẩy cho mỗi bè. Dòng mới giữa mỗi bè.

Làm thế nào bạn thực hiện quy tắc thứ ba là tùy thuộc vào bạn, nhưng điều này có thể liên quan đến việc tìm ra tỷ lệ chiếm hữu trung bình và giảm thiểu độ lệch khỏi nó càng nhiều càng tốt. Dưới đây là một số trường hợp thử nghiệm.

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]

Áp dụng quy tắc chuẩn, mã ngắn nhất sẽ thắng. Chúc vui vẻ!

Đã chỉnh sửa; Một cách được đề xuất để xác định càng nhiều càng tốt cho quy tắc thứ ba.

Khi số lượng bè rđã được xác định (theo quy tắc thứ hai), công suất trung bình acó thể được tính bằng cách tính tổng các đặt chỗ và chia cho r. Đối với mỗi bè, độ lệch so với công suất trung bình có thể được tìm thấy bằng cách sử dụng d(x) = abs(n(x)-a), trong đó n(x)số người trong mỗi bè và 1 <= x <= r. Đối với một số hàm liên tục, có giá trị duy nhất, có giá trị f(y)dương hoàn toàn và có đạo hàm thứ hai dương và thứ nhất dương cho tất cả các số dương y, chúng tôi xác định một đại lượng không âm F, là tổng của tất cả f(d(x)), 1 <= x <= r. Bất kỳ lựa chọn phân bổ bè nào thỏa mãn hai quy tắc đầu tiên và ở đâu Fbằng với mức tối thiểu toàn cầu cũng sẽ thỏa mãn quy tắc thứ ba.


3
Để tham khảo trong tương lai, bạn có thể đăng lên hộp cát của chúng tôi để nhận phản hồi về thử thách trước khi đăng.
Thuật sĩ lúa mì

Chào mừng bạn đến với Câu đố lập trình & Code Golf! Đây có vẻ là một thử thách tốt, biết rằng đó là thử thách đầu tiên của bạn. Tuy nhiên, lần tới, có thể tốt hơn là đăng thử thách trong Sandbox trước, vì vậy mọi người có thể đưa ra đề xuất ở đó. Sau đó, khi bạn nghĩ rằng thử thách đã xong, bạn có thể đăng nó lên trang web chính. Cảm ơn bạn đã đọc, và có một ngày tốt đẹp!
Matthew Roh

Làm thế nào là càng bình đẳng càng tốt đo?
Dennis

@Dennis; Tôi sẽ đặt một cách được đề xuất để xác định điều này trong một chỉnh sửa. Tuy nhiên nếu bạn có một phương pháp khác, và có thể biện minh cho câu trả lời của bạn, điều đó là tốt.
Gwyn

1
Để lại mọi thứ cho đến khi thực hiện là tốt, miễn là nó rõ ràng cái gì là hợp lệ và cái gì không, và bản chỉnh sửa mới nhất của bạn đạt được điều đó. Mặc dù vậy, tôi hơi ngạc nhiên rằng chúng ta không thể sử dụng g(y) = y(dẫn xuất số 0) hoặc g(y) = y²(lần đầu tiên thay đổi số 0 khi y = 0).
Dennis

Câu trả lời:


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})}

Hãy thử trực tuyến!

Làm thế nào nó hoạt động

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

    Tạo tất cả các phân vùng có thể của tất cả các hoán vị của mảng đầu vào.

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

    Lọc những cái mà không có bè được đặt quá nhiều.

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

    Lọc những cái mà số lượng bè là tối thiểu.

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

    Lấy cái đầu tiên với tối thiểu ∑ (n x -a) 2 .

-4 byte nhờ @ Pietu1998


Bạn có cần phải làm .absnếu bạn bình phương kết quả?
PurkkaKoodari

@ Pietu1998: Tôi không, bắt tốt.
smls

3

Haskell 226 228 234 268 byte

Câu trả lời ngây thơ trong 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

Hoặc vô lương tâm

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

Với một số trường hợp thử nghiệm

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

testSản lượng ở đâu

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]]

Chỉnh sửa

Cảm ơn @flawr và @nimi cho lời khuyên.

Bóp pmột chút.

Cạo đi một vài byte.


1
Bạn có thể thiết lập s=sumvà sau đó sử dụng sthay vì sumvà có lẽ bạn cũng có thể thay thế fst$ ...bằng ...!!0.
flawr

1
Bạn có thể thay thế minimumBy(c s)bằng head.sortOn svà loại bỏ chức năng c. Ngoài ra: \t->sum t<=n(<=n).sum.
nimi

@flawr, đề nghị tốt, cảm ơn!
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))

Với tủ thử nghiệm:

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))

Làm thế nào nó hoạt động?

Các pchức năng đơn giản là tạo ra tất cả các phân vùng của một danh sách nhất định (tất cả các cách có thể chia nó ra thành các danh sách con). s=sumchỉ cần đổi tên hàm sum, vì vậy dòng cuối cùng thực hiện tất cả công việc.

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.

Tôi chắc chắn rằng điều này có thể được chơi gôn hơn nữa, đặc biệt là pchức năng, nhưng tôi đã làm việc này hàng giờ rồi, vì vậy bạn hãy vào đây.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.