让我们建立一个赛车轨道!


19

介绍

我的外女想要赛车。她的木部分可以组合成轨道。每个零件均为正方形,并包含不同的形状。我将使用管道绘图字符进行说明:

  • :垂直走的路
  • :水平行驶的道路
  • :转向的道路
  • :带有地下通道的桥梁

奇怪的是,没有t型接头。

这是一个可能的赛车轨迹的示例:

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

有效赛车轨道的规则如下:

  • 没有路可走。
  • 它必须形成一个循环(所有部分都必须是同一循环的一部分)。
  • 在桥梁/地下通道,您不能转弯(因此,您必须直接穿过它们)。

不幸的是,我和我的侄女所拥有的赛车履带有限。但是我们绝对希望在赛道上使用所有这些。编写一个程序,给定我们库存中有哪些零件的列表,并输出使用所有这些零件的赛车轨道。

输入说明

我们希望输入通过STDIN,命令行参数,文件读取或用户输入函数(例如raw_inputprompt)输入。输入是逗号分隔的正整数,格式为

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

其中每个代表我们所拥有的特定件数。因此,例如输入:

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

意味着我们每个都有一个。

输出说明

使用上面列出的管道图字符输出赛车轨道。赛车轨道应准确使用输入中指定的每个零件的编号-不得多于或少于。每次输入至少会有一条有效的赛车轨迹。

输入和输出示例

输入: 3,5,2,2,2,2,1

可能的输出:

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

输入: 0,0,1,4,4,1,3

可能的输出:

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

是否需要提供输出?还是仅在理论上需要给出输出?
Sumurai15年

@ Sumurai8“理论上”给出输出是什么意思?您的意思是程序不会终止很长时间,但最终会给出输出吗?
2015年

1
一个人可能可以创建一个nxn的正方形区域,其中填充了竞赛部分和空的正方形,您可以在其中生成排列,直到找到赛道。这将永远花费不止几个瓷砖。
Sumurai15年

4
@ Sumurai8好的,我现在明白了。对于在挑战中显示的小值输入,我希望程序在宇宙热死之前给出输出。
2015年

4
你侄女不够耐心!:P
Sumurai15年

Answers:


4

红宝石664 671 677 687 701(678个字节)

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

这不是我能想到的最短的程序,但是我牺牲了一些简短的执行速度。

您可以在此处尝试该程序。请注意,ideone具有执行时间限制,因此对于包含大约12个以上的输入,该程序可能会超时。

该程序还有一个测试套件。请注意,由于上述时间限制,最后两个测试在ideone上被禁用。要启用这些测试,请x_从其名称中删除前缀。

该程序使用深度优先搜索来找到解决方案。它一次放置一块,并跟踪松散的末端。当不再有松散(未连接)的末端并且所有部件都已放置时,搜索将停止。

这是非高尔夫程序:

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))
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.