aboutsummaryrefslogtreecommitdiff
path: root/2025/10/b.py
blob: 57fff46bcf567dc2d5a9ae0d09ad762a1779ddaf (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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import fileinput
from math import gcd

inf = 999999999

# Read an input line and return a triple A, b, c where:
#   A is the matrix of coefficient (A[i][j] is 1 if pressing the j-th button
#     increases the i-th counter, 0 otherwise).
#   b is the list of required final values of each counter.
#   c is a list of bounds for the number of times each button can be pressed:
#     c[i] is the minimum required value of a counter increased by button i.
def readline(l):
	b = [int(x) for x in l[l.index('{')+1:l.index('}')].split(',')]

	A = [[] for j in b]
	c = []

	end = l.index(']')
	while '(' in l[end:]:
		begin = l[end:].index('(') + end + 1
		end = l[begin:].index(')') + begin
		bu = [int(x) for x in l[begin:end].split(',')]
		c.append(min(b[j] for j in bu))
		for i in range(len(b)):
			A[i].append(1 if i in bu else 0)

	return A, b, c

# swaprow, swapcol and reducerow are utility functions for the matrix
# row reduction procedure.
def swaprow(A, b, i, j):
	if i != j:
		A[i], A[j] = A[j], A[i]
		b[i], b[j] = b[j], b[i]

def swapcol(A, c, i, j):
	if i != j:
		for k in range(len(A)):
			A[k][i], A[k][j] = A[k][j], A[k][i]
		c[i], c[j] = c[j], c[i]

def reducerow(A, b, i, j):
	if A[i][i] != 0:
		x = A[i][i]
		y = -A[j][i]
		d = gcd(x, y)
		A[j] = [(y*A[i][k]+x*A[j][k])//d for k in range(len(A[i]))]
		b[j] = (y*b[i]+x*b[j])//d

# Reduce the matrix A by row, including back subsitution. Returns a triple
# A, b, c where:
#   A is the reduced matrix in the form [D|p] with D diagonal and p a matrix
#     whose number of columns is the number of free parameters for Ax = b.
#   b and c are the adjusted versions of the input paramters with the same
#     names. In particular, b is adjusted every time a row is reduced and
#     when rows are swapped, while c (the list of bounds) is adjusted when
#     two columns are swapped (which is equivalent to changing the order of
#     two buttons).
def reduce(A, b, c):
	for i in range(len(A[0])):
		# Swap columns until there is one in position i with at least
		# one non-zero element.
		I = []
		k = i
		while len(I) == 0 and k < len(A[0]):
			swapcol(A, c, i, k)
			I = [j for j in range(i, len(A)) if A[j][i] != 0]
			k += 1

		# If no such column is found, we are done
		if len(I) == 0:
			break

		# Swap rows so that A[i][i] is non-zero
		swaprow(A, b, i, I[0])

		# Reduce all other rows
		for j in range(i+1, len(A)):
			reducerow(A, b, i, j)

	# Remove all rows of zero and check if the system is solvable
	I = [i for i in range(len(A)) if any(a != 0 for a in A[i])]
	A = [A[i] for i in I]
	b = [b[i] for i in I]

	# Back substitution
	for i in range(len(A)-1, -1, -1):
		for j in range(i):
			reducerow(A, b, i, j)

	return A, b, c

# Find all combinations of parameters respecting the bounds in c. The
# free parameters are always the last n-rank(A) columns of the matrix A
# (where n is the total number of columns).
def paramcomb(nparam, c):
	if nparam == 0:
		return [[]]

	ret = []
	for i in range(c[-nparam]+1):
		ret += [[i, *l] for l in paramcomb(nparam-1, c)]
	return ret

# Solve the integer linear system Ax = b, minimizing the sum of the
# coordinates of x.
def solve_system_min_sum(A, b, c):
	k = len(A[0]) - len(A)
	mins = inf
	for c in paramcomb(k, c):
		sol = sum(c)
		for i in range(len(A)):
			cc = (c[j]*A[i][len(A[0])-k+j] for j in range(len(c)))
			s = b[i]-sum(cc)
			a = s // A[i][i]
			# If a is negative or not integer, we skip this solution
			if a < 0 or s % A[i][i] != 0:
				sol = inf
				break
			sol += a
		mins = min(mins, sol)
	return mins

with fileinput.input() as lines:
	print(sum(solve_system_min_sum(*reduce(*readline(l))) for l in lines))

Generated with cgit - Back to sebastiano.tronto.net