Ruby - około 700 golfistów. Zacząłem grę w golfa z jednoznakowymi nazwami zmiennych i metod, ale po pewnym czasie bardziej zainteresowałem się algorytmem niż golfem, więc przestałem próbować optymalizować długość kodu. Nie martwiłem się też o ciąg wejściowy. Mój wysiłek jest poniżej.
Aby pomóc ci zrozumieć, jak to działa, zamieściłem komentarze pokazujące, w jaki sposób manipulowany jest określony ciąg (u = "2 1 4 3 0 3 4 4 3 5 0 3"). Wymieniam kombinacje „skał w strumieniu”, które można przeskoczyć. Są one reprezentowane przez ciąg binarny. W komentarzach podam przykład 0b0101101010 i pokazuję, jak by go wykorzystano. Te 1 odpowiadają pozycjom skał dostępnych podczas pierwszej podróży; zera dla podróży powrotnej. Do każdego takiego przydziału używam programowania dynamicznego, aby określić minimalną liczbę przeskoków wymaganych w każdym kierunku. Przeprowadzam również kilka prostych optymalizacji, aby wcześnie wyeliminować niektóre kombinacje.
Uruchomiłem go z ciągami podanymi w innych odpowiedziach i uzyskałem te same wyniki. Oto kilka innych wyników, które uzyskałem:
"2 1 4 3 0 3 4 4 3 5 0 3" # => 8
"3 4 3 5 6 4 7 4 3 1 5 6 4 3 1 4" # => 7
"2 3 2 4 5 3 6 3 2 0 4 5 3 2 0 3" # => 10
"3 4 3 0 4 3 4 4 5 3 5 3 0 4 3 3 0 3" # => 11
"2 3 2 4 5 3 6 3 2 0 4 5 3 2 0 3 4 1 6 3 8 2 0 5 2 3" # => 14
Chciałbym dowiedzieć się, czy inni uzyskają takie same wyniki dla tych ciągów. Wydajność jest dość dobra. Na przykład znalezienie rozwiązania dla tego ciągu zajęło mniej niż minutę:
„3 4 3 0 4 3 4 4 5 3 5 3 0 4 3 3 0 3 4 5 3 2 0 3 4 1 6 3 2 0 4 5 3 2 0 3 4 1 6 3 0 4 3 4 4 5 0 1”
Kod następuje.
I=99 # infinity - unlikely we'll attempt to solve problems with more than 48 rocks to step on
def leap!(u)
p = u.split.map(&:to_i) # p = [2,1,4,3,0,3,4,4,3,5,0,3]
s = p.shift # s=2, p = [1,4,3,0,3,4,4,3,5,0,3] # start
f = p.pop # f=3, p = [1,4,3,0,3,4,4,3,5,0] # finish
q = p.reverse # q = [0,5,3,4,4,3,0,3,4,1] # reverse order
# No path if cannot get to a non-zero rock from s or f
return -1 if t(p,s) || t(q,f)
@n=p.size # 10 rocks in the stream
r = 2**@n # 10000000000 - 11 binary digits
j = s > @n ? 0 : 2**(@n-s) # 100000000 for s = 2 (cannot leave start if combo number is smaller than j)
k=r-1 # 1111111111 - 10 binary digits
b=I # best number of hops so far (s->f + f->s), initialized to infinity
(j..k).each do |c|
# Representative combo: 0b0101101010, convert to array
c += r # 0b10 1 0 1 1 0 1 0 1 0
c = c.to_s(2).split('').map(&:to_i)
# [1,0,1,0,1,1,0,1,0,1,0]
c.shift # [0,1,0,1,1,0,1,0,1,0] s->f: rock offsets available: 1,3,4,6,8
d = c.map {|e|1-e}.reverse # [1,0,1,0,1,0,0,1,0,1] f->s: rock offsets available: 0,2,5,7,9
c = z(c,p) # [0,4,0,0,3,0,4,0,5,0] s->f: max hops by offset for combo c
d = z(d,q) # [0,0,3,0,4,0,0,3,0,1] f->s: max hops by offset for combo c
# Skip combo if cannot get from to a rock from f, or can't
# get to the end (can always get to a rock from s if s > 0).
next if [s,f,l(c),l(d)].max < @n && t(d,f)
# Compute sum of smallest number of hops from s to f and back to s,
# for combo c, and save it if it is the best solution so far.
b = [b, m([s]+c) + m([f]+d)].min
end
b < I ? b : -1 # return result
end
# t(w,n) returns true if can conclude cannot get from sourch n to destination
def t(w,n) n==0 || (w[0,n].max==0 && n < @n) end
def l(w) w.map.with_index {|e,i|i+e}.max end
def z(c,p) c.zip(p).map {|x,y| x*y} end
def m(p)
# for s->f: p = [2,0,4,0,0,3,0,4,0,5,0] - can be on rock offsets 2,5,7,9
# for f->s: p = [3,0,0,3,0,4,0,0,3,0,1] - can be on rock offsets 3,5,8,10
a=[{d: 0,i: @n+1}]
(0..@n).each do |j|
i=@n-j
v=p[i]
if v > 0
b=[I]
a.each{|h| i+v < h[:i] ? break : b << (1 + h[:d])}
m = b.min
a.unshift({d: m,i: i}) if m < I
end
end
h = a.shift
return h[:i]>0 ? I : h[:d]
end
Thus, it should be clear that one can always jump from the last position.
- czy nie1 0
jest kontrprzykład?