Dzisiaj nie chciało mi się kombinować więc zadanie rozwiązałem po prostu w Pythonie.
Zadanie
Treść nie jest była dzisiaj skomplikowana, w każdej lini otrzymujemy liczbę i listę liczb,
pomiędzy liczby z listy musimy umieścić operatory (+
albo *
), w taki sposób żeby uzyskać
pierwszą liczbę w linii. (operacje wykonywane są od lewej do prawej).
190: 10 19 # 190 = 10 * 19
100: 3 60 10 # nie da się
83: 17 5 # nie da się
4: 1 1 2 # 4 = (1 + 1) * 2
Rozwiązanie
Do sprawdzenia czy dana linia jest poprawna (możemy w niej dobrać odpowiednie operatory), napisałem prostą rekurencyjną funkcję. Sprawdza ona wszystkie kombinacje operatorów, aż znajdzie ten poprawny.
def is_valid(target, acc, arr):
if not arr:
return target == acc
return (
is_valid(target, acc + arr[0], arr[1:]) or
is_valid(target, acc * arr[0], arr[1:])
)
Reszta kodu to tylko parsowanie wejścia i zliczanie poprawnych lini oraz wypisanie wyniku:
import fileinput # z biblioteki standardowej
res = 0
for line in fileinput.input(): # czytamy stdin
s = line.strip().split()
num = int(s[0][:-1])
arr = list(map(int, s[1:]))
if is_valid(num, arr[0], arr[1:]):
res += num
print(res)
Otrzymujemy poprawny wynik!
Część druga
Nie różni się ona bardzo od pierwszej, dochodzi nam jedynie jeszcze jeden możliwy
operator. Konkatenacja (||
), np. 10 || 4 = 104
, jedyne zmiany jakie musimy wprowadzić,
będą w funkcji is_valid
def is_valid(target, acc, arr):
if not arr:
return target == acc
return (
is_valid(target, acc + arr[0], arr[1:]) or
is_valid(target, acc * arr[0], arr[1:]) or
is_valid(target, int(str(acc) + str(arr[0])), arr[1:])
)
Działa, ale dość wolno (2 sekundy), jesteśmy w stanie na pewno zoptymalizować nasze rozwiązanie, pierwzszym pomysłem jest zakończenie przeszukania w momencie, w którym przekroczymy wartość poszukiwaną, działa to ponieważ wszystkie wszystkie wartości są dodatnie, a nasze operatory gwarantują że każda kolejna wartość będzie większa.
def is_valid(target, acc, arr):
if not arr:
return target == acc
if acc > target:
return False
return (
is_valid(target, acc + arr[0], arr[1:]) or
is_valid(target, acc * arr[0], arr[1:]) or
is_valid(target, int(str(acc) + str(arr[0])), arr[1:])
)
Ta prosta zmiana ściąga czas wykonywania do 1.2 sekundy.
Optymalizacja pamięci
Minus naszej poprzedniej funkcji jest taki że w każdym wykonaniu funkcji tworzymy slice
z naszej listy, gdzie tak na prawdę wymagamy jedynie wglądu w wartości na poszczególnych
pozycjach. Dlatego możemy trzymać w zmiennej pos aktualną pozycję w liście i przekazywać
też wskaźnik do całej listy arr
:
def is_valid(target, acc, pos, arr):
if pos == len(arr):
return target == acc
if acc > target:
return False
return (
is_valid(target, acc + arr[pos], pos + 1, arr) or
is_valid(target, acc * arr[pos], pos + 1, arr) or
is_valid(target, int(str(acc) + str(arr[pos])), pos + 1, arr)
)
Ta optymalizacja pozwala zejść nam do 0.5 sekund.