漂流问题(背包变体)


20

我的第一个难题是,很高兴收到改进建议!

场景是;您是一家激流漂流公司的经理。每天早晨,您都会收到一份预订清单,您必须将它们分类为木筏装载。用您选择的语言编写适合您的程序或函数。

每个木筏最多可容纳一个n客户,并且每个预订的n人数是1至2 (含)之间的一组。必须遵守以下规则;

  • 不得拆分任何组。如果他们一起订票,他们必须都在同一排。

  • 筏子的数量必须最小化。

  • 在符合前两个规则的前提下,组必须在木筏之间尽可能平均地分布。

输入。 数字n(您可能会认为这是一个正整数),以及所有预订的大小。如果您的语言支持,则可以是数组,列表或类似的数据结构。所有这些都是介于1和之间的正整数n。预订的顺序没有定义,也不重要。

输出。 预订编号列表,分为筏负载。必须明确指出分组,例如;

  • 列表或数组数组。
  • 每个筏的逗号分隔列表。每个筏之间的换行符。

如何实施第三条规则由您决定,但这可能涉及找到平均木筏占用率,并尽可能减少其偏离。这是一些测试用例。

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]

适用标准规则,以最短的代码为准。玩得开心!

编辑; 建议为第三条规则尽可能平等地定义的方法。

一旦r确定了木筏的数量(服从第二条规则),则a可以通过对预订进行求和并除以来计算平均占用率r。对于每个筏,可以使用找出与平均占用率的偏差d(x) = abs(n(x)-a),其中n(x)每个筏中的人数是和1 <= x <= r。对于某些连续的单值函数f(y),该函数严格为正,并且对于所有正数具有严格为正的一阶和非负二阶导数y,我们定义一个非负数F作为所有之和f(d(x)), 1 <= x <= r。满足前两个规则,且F等于全局最小值的筏选择的任何选择也将满足第三个规则。


3
为了将来参考,您可以在发布之前将其发布到我们的沙盒中,以获取有关挑战的反馈。
小麦巫师

欢迎来到编程难题和代码高尔夫球!知道这是您的第一个挑战,这似乎是一个不错的挑战。但是,下次最好将挑战首先发布在沙箱中,以便人们可以在那里提出建议。然后,当您认为挑战已完成时,可以将其发布在主站点上。感谢您的阅读,并祝您愉快!
马修·罗

如何尽可能平均地衡量?
丹尼斯,

@丹尼斯; 我将在编辑中提出一种建议的方式来定义它。但是,如果您使用其他方法,并且可以为您的答案辩护,那很好。
Gwyn

1
只要清楚什么是有效的,什么不是有效的,让事情留给实现是可以的,并且您的最新编辑可以实现该imo。我有点惊讶我们不能使用g(y) = y(第二个导数为零)或g(y) = y²(第一个导数为0时y = 0)。
丹尼斯,

Answers:


2

Perl 6的163个 158字节

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

在线尝试!

怎么运行的

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

    生成输入数组所有排列的所有可能分区。

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

    筛选没有筏超额预定的地方。

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

    筛选木筏数量最少的木筏。

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

    获取具有最小∑(n x -a)2的第一个。

-4个字节,感谢@ Pietu1998


.abs如果对结果求平方,是否需要做?
PurkkaKoodari

@ Pietu1998:不,很好。
smls

3

Haskell的226 228 234 268字节

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

或不打高尔夫球

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

有一些测试用例

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

哪里test产量

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

编辑

感谢@flawr和@nimi的建议。

p了一下。

削减了几个字节。


1
您可以设置s=sum,然后使用s代替sum,也许您也可以替换fst$ ......!!0
瑕疵的

1
您可以minimumBy(c s)使用head.sortOn s和删除功能c。另外:\t->sum t<=n(<=n).sum
nimi

@flawr,很好的建议,谢谢!
walpen

0

Python3,224个字节

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

使用测试用例:

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

它是如何工作的?

p函数仅生成给定列表的所有分区(将其划分为子列表的所有可能方式)。s=sum只是简单地重命名了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))
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.

我敢肯定,这可以做得更好,尤其是p功能方面,但是我已经花了几个小时研究了,所以这里就开始了。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.