Динамическое программирование с большим количеством подзадач. Поэтому я пытаюсь решить эту проблему с улицы Интервью:
Ходьба по сетке (оценка 50 баллов)
Вы находитесь в мерной сетке в позиции . Размеры сетки: ). За один шаг вы можете идти на один шаг вперед или назад в любом из измерений. (Так что всегда есть возможных разных ходов). Сколько способов вы можете сделать шагов, чтобы не покинуть сетку ни в одной точке? Вы выходите из сетки, если для любого , либо либо .
Моей первой попыткой было это запомненное рекурсивное решение:
def number_of_ways(steps, starting_point):
global n, dimensions, mem
#print steps, starting_point
if (steps, tuple(starting_point)) in mem:
return mem[(steps, tuple(starting_point))]
val = 0
if steps == 0:
val = 1
else:
for i in range(0, n):
tuple_copy = starting_point[:]
tuple_copy[i] += 1
if tuple_copy[i] <= dimensions[i]:
val += number_of_ways(steps - 1, tuple_copy)
tuple_copy = starting_point[:]
tuple_copy[i] -= 1
if tuple_copy[i] > 0:
val += number_of_ways(steps - 1, tuple_copy)
mem[(steps, tuple(starting_point))] = val
return val
Большой сюрприз: он не подходит для большого количества шагов и / или измерений из-за недостатка памяти.
Так что следующий шаг , чтобы улучшить свое решение с помощью динамического программирования. Но прежде чем начать, я вижу большую проблему с подходом. Аргумент starting_point
является -кратным, где таким же, как . Таким образом , в самом деле, функция может быть с .n 10 1 ≤ x i ≤ 100number_of_ways(steps, x1, x2, x3, ... x10)
Проблемы динамического программирования, которые я видел в учебниках, почти все имеют переменные twp, так что нужна только двумерная матрица. В этом случае потребуется десятимерная матрица. Итак, всего клеток.
С 2-D матрицами в динамическом программировании, как правило , только предыдущий ряд вычислений требуются для следующего расчета, следовательно , уменьшение пространственной сложности от до . Я не уверен, как бы я поступил так же в этом случае. Визуализация таблицы неосуществима, поэтому ответ должен прийти непосредственно из приведенной выше рекурсии.
ОБНОВИТЬ
Используя предложения Питера Шора и внося некоторые незначительные исправления, в частности, необходимость отслеживать положение в функции , а не только измерения на два набора A и B, выполняя разбиение рекурсивно, эффективно используя метод «разделяй и властвуй», пока не будет достигнут базовый случай, когда в наборе есть только одно измерение.
Я придумал следующую реализацию, которая прошла все тесты ниже максимального времени выполнения:
def ways(di, offset, steps):
global mem, dimensions
if steps in mem[di] and offset in mem[di][steps]:
return mem[di][steps][offset]
val = 0
if steps == 0:
val = 1
else:
if offset - 1 >= 1:
val += ways(di, offset - 1, steps - 1)
if offset + 1 <= dimensions[di]:
val += ways(di, offset + 1, steps - 1)
mem[di][steps][offset] = val
return val
def set_ways(left, right, steps):
# must create t1, t2, t3 .. ti for steps
global mem_set, mem, starting_point
#print left, right
#sleep(2)
if (left, right) in mem_set and steps in mem_set[(left, right)]:
return mem_set[(left, right)][steps]
if right - left == 1:
#print 'getting steps for', left, steps, starting_point[left]
#print 'got ', mem[left][steps][starting_point[left]], 'steps'
return mem[left][steps][starting_point[left]]
#return ways(left, starting_point[left], steps)
val = 0
split_point = left + (right - left) / 2
for i in xrange(steps + 1):
t1 = i
t2 = steps - i
mix_factor = fact[steps] / (fact[t1] * fact[t2])
#print "mix_factor = %d, dimension: %d - %d steps, dimension %d - %d steps" % (mix_factor, left, t1, split_point, t2)
val += mix_factor * set_ways(left, split_point, t1) * set_ways(split_point, right, t2)
mem_set[(left, right)][steps] = val
return val
import sys
from time import sleep, time
fact = {}
fact[0] = 1
start = time()
accum = 1
for k in xrange(1, 300+1):
accum *= k
fact[k] = accum
#print 'fact_time', time() - start
data = sys.stdin.readlines()
num_tests = int(data.pop(0))
for ignore in xrange(0, num_tests):
n_and_steps = data.pop(0)
n, steps = map(lambda x: int(x), n_and_steps.split())
starting_point = map(lambda x: int(x), data.pop(0).split())
dimensions = map(lambda x: int(x), data.pop(0).split())
mem = {}
for di in xrange(n):
mem[di] = {}
for i in xrange(steps + 1):
mem[di][i] = {}
ways(di, starting_point[di], i)
start = time()
#print 'mem vector is done'
mem_set = {}
for i in xrange(n + 1):
for j in xrange(n + 1):
mem_set[(i, j)] = {}
answer = set_ways(0, n, steps)
#print answer
print answer % 1000000007
#print time() - start
источник
mem[]
словарь. И спасибо, что убрали мой ответ. Не слишком знаком с LaTeX, но постараюсь в следующий раз.Ответы:
Различные размеры независимы . Что вы можете сделать, так это вычислить, для каждого измерения j , сколько различных шагов есть только в этом измерении, которые предпринимают шагов. Назовем это число . Из вашего вопроса вы уже знаете, как вычислить эти числа с помощью динамического программирования.t W(j,t)
Теперь легко подсчитать количество прогулок, которые делают шагов в измерении . У вас есть способов вкрапления измерений, так что общее количество шагов, сделанных в измерении равно , и для каждого из этих способов у вас есть . Суммируйте их, чтобы получить Теперь память находится под контролем, так как вам нужно только запомнить значения . Время увеличивается сверхполиномиально для больших , но у большинства компьютеров гораздо больше времени, чем памяти.ti i (Nt1,t2,…,tM) i ti ΠN1W(i,ti)
Вы можете сделать еще лучше. Рекурсивный разделить размеры на два подмножества, и , и вычислить , сколько ходит там , используя только размеры в подмножество , и только те , в . Назовите эти номера и соответственно. Вы получаете общее количество прогулок поA B A B WA(t) WB(t)
источник
Давайте из вашего кода формулу для (для внутренней ячейки, которая игнорирует граничные случаи):now(s,x1,…,xn)
Вот несколько идей.
Этого должно быть достаточно, чтобы сохранить использование памяти достаточно низким.
источник