aboutsummaryrefslogtreecommitdiff
path: root/challenge-036/archargelod/nim/ch_2.nim
blob: 3a3c3fdeb2b4ab116ab25abf8989d4e023988074 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#!/usr/bin/env -S nim r -d:release --verbosity:0 --hints:off
import std/[strutils, sequtils, math]

type Box = object
  label: string
  weight, amount: int

proc `$`*(b: Box): string =
  b.label

proc allSubsets[T](baseSet: openarray[T]): seq[seq[T]] =
  for i in 1 ..< 2 ^ baseSet.len:
    let pattern = i.toBin(baseSet.len)
    result.add @[]
    for j, bit in pattern:
      if bit == '1':
        result[^1].add baseSet[j]

proc bestCombinations*(boxes: openarray[Box], maxWeight, maxBoxes: int): seq[seq[Box]] =
  var bestValue: int

  let subsetsB = boxes.toOpenArray(boxes.len div 2 + 1, boxes.high).allSubsets()
  for subsetA in boxes.toOpenArray(0, boxes.len div 2).allSubsets():
    let weightA = subsetA.mapIt(it.weight).sum()
    let valueA = subsetA.mapIt(it.amount).sum()

    for subsetB in subsetsB:
      let combinedWeight = weightA + subsetB.mapIt(it.weight).sum()
      let combinedValue = valueA + subsetB.mapIt(it.amount).sum()
      if (subsetA.len + subsetB.len) <= maxBoxes and combinedWeight <= maxWeight:
        if combinedValue > bestValue:
          bestValue = combinedValue
          result = @[subsetA & subsetB]
        elif combinedValue == bestValue:
          result.add subsetA & subsetB

when isMainModule:
  import std/unittest

  const
    Boxes = [
      Box(label: "R", weight: 1, amount: 1),
      Box(label: "B", weight: 1, amount: 2),
      Box(label: "G", weight: 2, amount: 2),
      Box(label: "Y", weight: 12, amount: 4),
      Box(label: "P", weight: 4, amount: 10),
    ]
    Expected = ["@[@[R, B, G, P]]", "@[@[B, G, P]]", "@[@[G, P], @[B, P]]"]

  suite "Knapsack Problem 0-1":
    test "5 or less boxes":
      check $bestCombinations(Boxes, 15, 5) == Expected[0]
    test "3 or less boxes":
      check $bestCombinations(Boxes, 15, 3) == Expected[1]
    test "2 or less boxes":
      check $bestCombinations(Boxes, 15, 2) == Expected[2]