경주 용 자동차 트랙을 만들자!


19

소개

내 조카가 경주 용 자동차 트랙을 만들고 싶어합니다. 그녀는 트랙을 형성하기 위해 함께 맞는 나무 부품을 가지고 있습니다. 각 부분은 정사각형 모양이며 다른 모양을 포함합니다. 파이프 그리기 문자를 사용하여 설명하겠습니다.

  • : 세로로가는 길
  • : 가로로가는 길
  • : 방향으로 향하는 도로
  • : 지하도의 다리

흥미롭게도 t- 접합 조각이 없습니다.

가능한 경주 용 자동차 트랙의 예는 다음과 같습니다.

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

유효한 경주 용 자동차 트랙의 규칙은 다음과 같습니다.

  • 아무데도 갈 수있는 길은 없습니다.
  • 루프를 형성해야합니다 (그리고 모든 조각은 동일한 루프의 일부 여야합니다).
  • 다리 / 지하도에서는 돌릴 수 없습니다 (따라서 직접 통과해야 함).

불행하게도, 나의 조카딸과 내가 가지고있는 경주 용 자동차 트랙 조각은 제한되어 있습니다. 그러나 우리는 트랙에서 모두 사용하고 싶습니다. 인벤토리에 어떤 조각이 있는지 목록을 통해 해당 조각을 모두 사용하는 경주 용 자동차 트랙을 출력 하는 프로그램 을 작성하십시오 .

입력 설명

STDIN, 명령 줄 인수, 파일 읽기 또는 사용자 입력 함수 (예 : raw_input또는 prompt) 를 통해 입력을 수신하려고 합니다. 입력은 쉼표로 구분 된 양의 정수 형식입니다.

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

여기서 각 부분은 우리가 보유한 특정 부분의 양을 나타냅니다. 예를 들어 입력 :

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

우리가 각 조각 중 하나를 가지고 있음을 의미합니다.

출력 설명

위에 나열된 파이프 그리기 문자를 사용하여 경주 용 자동차 트랙을 출력하십시오. 경주 용 자동차 트랙은 입력에 지정된 각 조각의 수를 더 이상 사용하지 않아야합니다. 모든 입력에 대해 하나 이상의 유효한 경주 용 자동차 트랙이 있습니다.

입력 및 출력 예

입력: 3,5,2,2,2,2,1

가능한 출력 :

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

입력: 0,0,1,4,4,1,3

가능한 출력 :

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

출력을 제공해야합니까? 아니면 이론적으로 만 출력을 제공해야합니까?
Sumurai8

@ Sumurai8 "이론적으로"출력한다는 것은 무슨 뜻입니까? 당신은 매우 오랫동안 종료되지 않지만 결국 결과를 줄 프로그램을 의미합니까?
압생트

1
레이스 조각과 빈 사각형으로 채워진 nxn 사각형의 필드를 만들 수 있습니다. 레이스 트랙 인 것을 찾을 때까지 순열을 생성 할 수 있습니다. 타일 ​​몇 개 이상을 위해서는 영원히 걸릴 것입니다.
Sumurai8

4
@ Sumurai8 아 알았어, 이제 이해해. 나는 프로그램이 내가 도전에서 보여준 작은 가치 입력에 대해 우주의 열사병 전에 산출물을 제공하는 것을 선호한다.
압생트

4
조카가 인내심이 부족합니다! : P
Sumurai8

답변:


4

루비 664671677 687701 (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 ​​개 이상의 입력으로 구성된 입력의 경우 프로그램이 시간 초과 될 수 있습니다.

프로그램을위한 테스트 스위트 도 있습니다 . 위에서 언급 한 시간 제한으로 인해 마지막 두 테스트는 IDE에서 비활성화됩니다. 이러한 테스트를 활성화하려면 x_해당 이름 에서 접두사를 삭제하십시오 .

프로그램은 깊이 우선 검색을 사용하여 솔루션을 찾습니다. 한 번에 하나씩 조각을 배치하고 느슨한 끝을 추적합니다. 더 이상 느슨한 (연결되지 않은) 끝이없고 모든 부품이 배치되면 검색이 중지됩니다.

이것은 ungolfed 프로그램입니다 :

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))
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.