summaryrefslogtreecommitdiff
path: root/24/main.py
blob: 61c97a6acdcf643d8cffca35bee0fbf12d5ff704 (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/python

from itertools import combinations



def solve_partn(partn, masses):
    if partn == 1:
        num_groups = 3
    else: # partn == 2
        num_groups = 4

    m_total = sum(masses)
    m_group = int(m_total / num_groups)

    # At first, it seems the easiest way is to generate all possible
    # grouping of packages, and then just keep the ones that are valid,
    # and find the best among the remaining configurations. However,
    # the input file has 28 packages, so there are 3^28 = 23 trillion
    # possible groupings, which is obviously too much to brute-force.

    # Instead, we find the candidates for group 1: subsets of the packages
    # whose masses add up to `m_group', with the smallest number of items.

    # Step 1: Find all combinations of packages in a certain range
    k_min = int((m_group - 1) / max(masses)) + 1
    k_max = int(len(masses) / num_groups)
    combos = []
    for k in range(k_min, k_max + 1):
        combos += combinations(masses, k)
    # Step 2: Keep only combinations whose masses add up to `m_group'
    group1 = []
    for c in combos:
        if sum(c) == m_group:
            group1.append(c)
    # Step 3: Keep only candidates with the smallest package count
    min_len = min(map(len, group1))
    smallest = {}
    for g1 in group1:
        if len(g1) == min_len:
            smallest[g1] = []

    # Verify for each group 1 candidate that the remaining packages can be
    # divided into 2 more groups of mass `m_group'. This takes time and it
    # turns out that no candidates get rejected, hence it's commented out.
    #for i, g1 in enumerate(smallest):
    #    remaining = []
    #    for m in masses:
    #        if m not in g1:
    #            remaining.append(m)
    #    k_max = int(len(remaining) / (num_groups - 1))
    #    combos = []
    #    for k in range(k_min, k_max + 1):
    #        combos += combinations(remaining, k)
    #    for c in combos:
    #        if sum(c) == m_group:
    #            smallest[g1].append(c)
    #valid = {}
    #for g1 in smallest.keys():
    #    if len(smallest[g1]) > 0:
    #        valid[g1] = smallest[g1]

    # Calculate quantum entanglement of each group 1 candidate
    qentangs = []
    for g1 in smallest.keys():
        qe = 1
        for m in g1:
            qe *= m
        qentangs.append(qe)

    return min(qentangs)



def main():
    # Read package weights from input text file
    with open("input.txt", "r") as f:
        masses = list(map(int, f.read().splitlines()))

    print("Part 1 solution:", solve_partn(1, masses)) # 10439961859 for me
    print("Part 2 solution:", solve_partn(2, masses)) #    72050269 for me



if __name__ == "__main__":
    main()