Programação dinâmica com grande número de subproblemas. Então, eu estou tentando resolver esse problema da Interview Street:
Grid Walking (Score 50 points)
Você está situado em uma grade dimensional na posição . As dimensões da grade são ). Em uma etapa, você pode dar um passo à frente ou atrás em qualquer uma das dimensões. (Portanto, sempre existem movimentos diferentes possíveis). De quantas maneiras você pode tomar etapas de modo que você não saia da grade em nenhum momento? Você sai da grade se, para qualquer , ou .
Minha primeira tentativa foi esta solução recursiva memorizada:
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
Grande surpresa: falha em um grande número de etapas e / ou dimensões devido à falta de memória.
Portanto, o próximo passo é melhorar minha solução usando programação dinâmica. Mas antes de começar, estou vendo um grande problema com a abordagem. O argumento starting_point
é um duplo, onde é tão grande quanto . Portanto, de fato, a função poderia estar number_of_ways(steps, x1, x2, x3, ... x10)
com .
Os problemas de programação dinâmica que eu já vi nos livros didáticos quase todos têm variáveis twp, de modo que apenas uma matriz bidimensional é necessária. Nesse caso, seria necessária uma matriz de dez dimensões. Então, células no total.
ATUALIZAR
Usando as sugestões de Peter Shor e fazendo algumas correções menores, notavelmente a necessidade de acompanhar a posição na função e, em vez de apenas dividir as dimensões em dois conjuntos A e B, fazendo a divisão recursivamente, efetivamente usando um método de dividir e conquistar, até que um caso base seja alcançado, onde apenas uma dimensão está no conjunto.
Eu vim com a seguinte implementação, que passou em todos os testes abaixo do tempo máximo de execução:
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[]
dicionário. E obrigado por limpar minha resposta. Não está muito familiarizado com o LaTeX, mas fará um esforço na próxima vez.