aboutsummaryrefslogtreecommitdiff
path: root/challenge-036/javier-luque/perl5/ch-2.pl
blob: d101cacbb72b46d7765a53378da6861c63736069 (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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#!/usr/bin/perl
# Test: ./ch2.pl
use strict;
use warnings;

# Box configurations
my $boxes = {
    R => { weight => 1,  amount => 1  },
    B => { weight => 1,  amount => 2  },
    G => { weight => 2,  amount => 2  },
    Y => { weight => 12, amount => 4  },
    P => { weight => 4,  amount => 10 },
};

# knapsack with unlimited boxes and 15 kg max
knapsack($boxes, 15, 0);

# knapsack with 2 3 4 boxes and 15kg max
knapsack($boxes, 15, 2);
knapsack($boxes, 15, 3);
knapsack($boxes, 15, 4);

sub knapsack {
    my ($boxes, $max_weight, $max_boxes) = @_;
    my $total_weight = 0;
    my $total_boxes  = 0;
    my $total_amount = 0;
    my $set_of_boxes;

    # Order the boxes by which
    # gives the most value, followed by weight.
    for my $key ( sort sort_value_weight keys %$boxes ) {
        my $box = $boxes->{$key};

        # While there is space or weight left
        while (1) {
            # Check for space or weight
            last unless
                $total_weight + $box->{weight} <=
                $max_weight;

            last unless
                !$max_boxes ||
                ($max_boxes && $total_boxes + 1 <=
                 $max_boxes);

            $total_boxes++;
            $set_of_boxes .= $key;
            $total_weight += $box->{weight};
            $total_amount += $box->{amount};
        }
    }

    print 'Max weight: ' . $max_weight;
    print ', max boxes: ' . $max_boxes
        if ($max_boxes);
    print '. Boxes in knapsack: ' .
        $set_of_boxes;
    print ' ' . $total_weight . 'kg ';
    print '£' . $total_amount . "\n";
}

# Sort function to sort by value then weight
sub sort_value_weight {
    my $value_a =
        $boxes->{$::a}->{amount} /
        $boxes->{$::a}->{weight};

    my $value_b =
        $boxes->{$::b}->{amount} /
        $boxes->{$::b}->{weight};

    my $weight_a =
        $boxes->{$::a}->{weight};

    my $weight_b =
        $boxes->{$::b}->{weight};

    if ( $value_b > $value_a ) {
        return 1;
    } elsif ( $value_b == $value_a ) {
        return ($weight_b > $weight_a) ? 1 : -1;
    } else {
        return -1;
    }
}