Hãy xây dựng một đường đua xe!


19

Giới thiệu

Cháu gái tôi muốn làm một đường đua xe. Cô ấy có các bộ phận bằng gỗ khớp với nhau để tạo thành đường đua. Mỗi phần có hình vuông và chứa một hình dạng khác nhau. Tôi sẽ sử dụng các ký tự vẽ ống để minh họa:

  • : con đường đi theo chiều dọc
  • : con đường đi theo chiều ngang
  • : những con đường rẽ theo một hướng
  • : Cây cầu có đường chui

Thật kỳ lạ, không có mảnh ghép.

Dưới đây là một ví dụ về đường đua xe có thể:

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

Các quy tắc cho một đường đua xe hợp lệ như sau:

  • Không thể có con đường nào đi đến đâu cả.
  • Nó phải tạo thành một vòng lặp (và tất cả các mảnh phải là một phần của cùng một vòng lặp).
  • Tại các cây cầu / cầu chui, bạn không thể rẽ (vì vậy bạn phải đi thẳng qua chúng).

Thật không may, chiếc xe đua theo dõi cháu gái tôi và tôi bị hạn chế. Nhưng chúng tôi chắc chắn muốn sử dụng tất cả chúng trong bản nhạc. Viết một chương trình , đưa ra một danh sách các mảnh trong kho của chúng tôi, đưa ra một đường đua xe sử dụng tất cả các mảnh đó.

Mô tả đầu vào

Chúng tôi muốn đầu vào đi vào thông qua STDIN, đối số dòng lệnh, đọc tệp hoặc chức năng nhập của người dùng (chẳng hạn như raw_inputhoặc prompt). Đầu vào là các số nguyên dương được phân tách bằng dấu phẩy ở dạng

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

trong đó mỗi cái đại diện cho số lượng của mảnh cụ thể mà chúng ta có. Vì vậy, ví dụ đầu vào:

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

có nghĩa là chúng tôi đã có một trong mỗi phần.

Mô tả đầu ra

Xuất ra một đường đua xe bằng cách sử dụng các ký tự vẽ ống được liệt kê ở trên. Đường đua xe hơi nên sử dụng chính xác số lượng của từng mảnh được chỉ định trong đầu vào - không hơn, và không ít hơn. Sẽ có ít nhất một đường đua xe hợp lệ cho mỗi đầu vào.

Ví dụ đầu vào và đầu ra

Đầu vào: 3,5,2,2,2,2,1

Một đầu ra có thể:

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

Đầu vào: 0,0,1,4,4,1,3

Một đầu ra có thể:

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

Có cần phải cung cấp đầu ra? Hay về mặt lý thuyết nó chỉ cần cho đầu ra?
Sumurai8

@ Sumurai8 Ý bạn là gì về "lý thuyết" cho đầu ra? Bạn có nghĩa là một chương trình sẽ không chấm dứt trong một thời gian rất dài nhưng cuối cùng sẽ cung cấp đầu ra?
absinthe

1
Người ta có thể có thể tạo ra một trường các ô vuông nxn chứa đầy các mảnh đua và các ô vuông trống, nơi bạn có thể tạo các hoán vị cho đến khi bạn tìm thấy thứ gì đó là đường đua. Điều đó sẽ mất mãi mãi cho bất cứ điều gì nhiều hơn một vài gạch.
Sumurai8

4
@ Sumurai8 À được rồi, giờ tôi hiểu rồi. Tôi muốn các chương trình sẽ cung cấp một đầu ra trước cái chết nhiệt của vũ trụ cho các đầu vào giá trị nhỏ mà tôi đã thể hiện trong thử thách.
absinthe

4
Cháu gái của bạn không đủ kiên nhẫn! : P
Sumurai8

Câu trả lời:


4

Ruby 664 671 677 687 701 (678 byte)

_={│:[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

Đây không phải là chương trình ngắn nhất tôi có thể nghĩ ra, nhưng tôi đã hy sinh một chút ngắn gọn cho tốc độ thực hiện.

Bạn có thể thử nghiệm với chương trình ở đây . Lưu ý rằng ideone có giới hạn thời gian thực hiện, vì vậy đối với các đầu vào bao gồm hơn 12 phần, chương trình có thể sẽ hết thời gian.

Ngoài ra còn có một bộ thử nghiệm cho chương trình. Lưu ý rằng hai bài kiểm tra cuối cùng bị vô hiệu hóa trên ideone, do giới hạn thời gian được đề cập ở trên. Để kích hoạt các thử nghiệm này, hãy xóa x_tiền tố khỏi tên của chúng.

Chương trình tìm một giải pháp bằng cách sử dụng tìm kiếm theo chiều sâu; nó đặt từng mảnh một và giữ dấu vết lỏng lẻo. Việc tìm kiếm dừng lại khi không còn kết thúc lỏng lẻo (không kết nối) và tất cả các phần đã được đặt.

Đây là chương trình vô văn hóa:

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))
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.