Bir yarış arabası pisti inşa edelim!


19

Giriş

Yeğenim bir yarış arabası pisti yapmak istiyor. Pisti oluşturmak için birbirine uyan ahşap parçaları var. Her bölüm kare şeklindedir ve farklı bir şekil içerir. Boru çizim karakterlerini göstermek için kullanacağım:

  • : dikey giden yol
  • : yatay giden yol
  • : bir yöne dönen yollar
  • : Altgeçitli bir köprü

İlginçtir ki, t-bağlantı parçaları yoktur.

İşte olası bir yarış arabası pistine bir örnek:

┌─┐
│ │┌─┐
│ └┼─┘
└──┘

Geçerli bir yarış arabası pisti için kurallar aşağıdaki gibidir:

  • Hiçbir yere giden yol olamaz.
  • Bir döngü oluşturmalıdır (ve tüm parçalar aynı halkanın parçası olmalıdır).
  • Köprülerde / alt geçitlerde dönemezsiniz (bu yüzden doğrudan içinden geçmeniz gerekir).

Ne yazık ki, yeğenim ve ben yarış arabası pist parçaları sınırlı. Ama kesinlikle hepsini pistte kullanmak istiyoruz. Envanterimizde bulunan parçaların bir listesi verildiğinde, bu parçaların tümünü kullanan bir yarış arabası pisti çıkaran bir program yazın .

Giriş Açıklaması

Girişin STDIN, komut satırı bağımsız değişkenleri, dosya okuma veya bir kullanıcı giriş işlevi ( raw_inputveya gibi prompt) yoluyla gelmesini istiyoruz . Girdi biçiminde virgülle ayrılmış pozitif tamsayılar

│,─,┌,┐,└,┘,┼

burada her biri sahip olduğumuz o parçanın miktarını temsil eder. Örneğin, girdi:

1,1,1,1,1,1,1

her parçadan birine sahip olduğumuz anlamına gelir.

Çıktı Açıklaması

Yukarıda listelenen boru çizim karakterlerini kullanarak bir yarış arabası pistinin çıktısını alın. Yarış arabası pisti, girişte belirtilen her parçanın sayısını tam olarak kullanmalıdır - daha fazla ve daha az olmamalıdır. Her bir giriş için en az bir geçerli yarış arabası pisti olacaktır.

Örnek Girişler ve Çıkışlar

Giriş: 3,5,2,2,2,2,1

Olası bir çıktı:

┌─┐
│ │┌─┐
│ └┼─┘
└──┘

Giriş: 0,0,1,4,4,1,3

Olası bir çıktı:

 ┌┐
 └┼┐
  └┼┐
   └┼┐
    └┘

Çıktı vermek gerekiyor mu? Yoksa sadece teorik olarak çıktıyı vermesi mi gerekiyor?
Sumurai8

@ Sumurai8 Çıktıyı “teorik olarak” kastederek ne demek istiyorsun? Son derece uzun bir süre için sonlandırılmayacak ancak sonuçta çıktı verecek bir program mı demek istediniz?
Absinthe

1
Muhtemelen bir yarış pisti olan bir şey bulana kadar permütasyon oluşturabileceğiniz yarış parçaları ve boş karelerle dolu bir nxn karesi alanı oluşturabilirsiniz. Bu birkaç fayans daha fazla bir şey için sonsuza kadar sürer.
Sumurai8

4
@ Sumurai8 Ah tamam, şimdi anlıyorum. Programların, meydan okumada gösterdiğim küçük değer girdileri için evrenin ısı ölümünden önce bir çıktı vermesini tercih ederim.
pelin

4
Yeğeniniz yeterince sabırlı değil! : P
Sumurai8

Yanıtlar:


4

Yakut 664 671 677 687 701 (678 bayt)

_={│:[1,4],─:[2,8],┌:[4,8],┐:[4,2],└:[1,8],┘:[1,2],┼:[1,4,2,8]}
s=->a,l,b{l==[]&&a==[]?b:(l.product(l).any?{|q,r|q,r=q[0],r[0];(q[0]-r[0])**2+(q[1]-r[1])**2>a.size**2}?!0:(w,f=l.pop
w&&v=!a.size.times{|i|y=_[x=a[i]]
f&&y&[f]==[]||(k=l.select{|p,d|w!=p||y&[d]==[]}
(y-[f]).map{|d|z=[w[0]+(d<2?-1:(d&4)/4),w[1]+(d==2?-1:d>7?1:0)]
g=d<3?d*4:d/4
b[z]?_[b[z]]&[g]!=[]||v=0:k<<[z,g]}
v||r=s[a[0...i]+a[i+1..-1],k,b.merge({w=>x})]
return r if r)}))}
c=eval"[#{gets}]"
r=s[6.downto(0).map{|i|[_.keys[i]]*c[i]}.flatten,[[[0,0],nil]],{}]
h=j=k=l=0
r.map{|w,_|y,x=w
h>x&&h=x
j>y&&j=y
k<x&&k=x
l<y&&l=y}
s=(j..l).map{|_|' '*(k-h+1)}
r.map{|w,p|y,x=w
s[y-j][x-h]=p.to_s}
puts s

Bu, karşılaşabileceğim en kısa program değil, ama yürütme hızı için biraz kıskançlıktan fedakarlık ettim.

Programı burada deneyebilirsiniz . İdeone'nin bir yürütme süresi sınırına sahip olduğunu unutmayın, bu nedenle yaklaşık 12 parçadan oluşan girdiler için program muhtemelen zaman aşımına uğrayacaktır.

Program için bir test paketi de var . Son iki testin yukarıda belirtilen zaman sınırı nedeniyle ideone'de devre dışı bırakıldığını unutmayın. Bu testleri etkinleştirmek için x_önekleri adlarından silin .

Program ilk derinlik aramasını kullanarak bir çözüm bulur; parçaları birer birer yerleştirir ve gevşek uçların izlerini tutar. Artık gevşek (bağlı olmayan) uç olmadığında ve tüm parçalar yerleştirildiğinde arama durur.

Bu kuralsız program:

N, W, S, E = 1, 2, 4, 8

# given a direction, find the opposite
def opposite (dir)
  dir < 3 ? dir * 4 : dir / 4
end

# given a set of coordinates and a direction,
# find the neighbor cell in that direction
def goto(from, dir)
  y, x = from

  dx = case dir
  when W then -1
  when E then 1
  else 0
  end

  dy = case dir
  when N then -1
  when S then 1
  else 0
  end

  [y+dy, x+dx]
end

CONNECTIONS = {
  ?│ => [N, S],
  ?─ => [W, E],
  ?┌ => [S, E],
  ?┐ => [S, W],
  ?└ => [N, E],
  ?┘ => [N, W],
  ?┼ => [N, S, W, E], 
}

BuildTrack =-> { 
  piece_types = CONNECTIONS.keys
  piece_counts = gets.split(?,).map &:to_i

  pieces = 6.downto(0).map{|i|piece_types[i]*piece_counts[i]}.join.chars

  def solve (available_pieces, loose_ends=[[[0,0],nil]], board={})

    return board if loose_ends==[] and available_pieces==[]

    # optimization to avoid pursuing expensive paths
    # which cannot yield a result.
    # This prunes about 90% of the search space
    c = loose_ends.map{ |c, _| c }
    not_enough_pieces = c.product(c).any? { |q, r| 
      ((q[0]-r[0])**2+(q[1]-r[1])**2) > available_pieces.size**2
    }
    return if not_enough_pieces

    position, connect_from = loose_ends.pop

    return unless position

    available_pieces.size.times do |i|
      piece = available_pieces[i]

      remaining_pieces = available_pieces[0...i] + available_pieces[i+1..-1]

      piece_not_connected_ok = connect_from && CONNECTIONS[piece] & [connect_from] == []
      next if piece_not_connected_ok

      new_loose_ends = loose_ends.select  { |pos, dir| 
        # remove loose ends that may have been 
        # fixed, now that we placed this piece
        position != pos || CONNECTIONS[piece] & [dir] == []
      }

      invalid_placement = false

      (CONNECTIONS[piece]-[connect_from]).map do |dir|
        new_pos = goto(position, dir)
        new_dir = opposite(dir)

        if board[new_pos]
          if CONNECTIONS[board[new_pos]] & [new_dir] != []
            # do nothing; already connected
          else
            # going towards an existing piece
            # which has no suitable connection
            invalid_placement = true
          end
        else
          new_loose_ends << [new_pos, new_dir]
        end
      end

      next if invalid_placement

      new_board = board.merge({position => piece})

      result = solve(remaining_pieces, new_loose_ends, new_board)
      return result if result
    end
    nil
  end

  def print_board board
    min_x = min_y = max_x = max_y = 0

    board.each do |position, _|
      y, x = position
      min_x = [min_x, x].min
      min_y = [min_y, y].min
      max_x = [max_x, x].max
      max_y = [max_y, y].max
    end

    str = (min_y..max_y).map{|_|
      ' ' * (max_x - min_x + 1)
    }

    board.each do |position, piece|
      y, x = position
      str[y-min_y][x-min_x] = piece
    end
    puts str
  end

  print_board(solve(pieces))
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.