มาสร้างลู่แข่งรถกันเถอะ!


19

บทนำ

หลานสาวของฉันต้องการติดตามรถแข่ง เธอมีชิ้นส่วนไม้ที่เข้าด้วยกันเพื่อสร้างแทร็ก แต่ละส่วนเป็นรูปสี่เหลี่ยมจัตุรัสและมีรูปร่างแตกต่างกัน ฉันจะใช้อักขระวาดท่อเพื่อแสดง:

  • : ถนนที่ไปในแนวตั้ง
  • : ถนนที่ไปในแนวนอน
  • : ถนนที่เลี้ยวไปในทิศทางหนึ่ง
  • : สะพานที่มีทางลอด

อยากรู้อยากเห็นไม่มีชิ้นส่วนทางแยกที

นี่คือตัวอย่างของลู่วิ่งรถแข่งที่เป็นไปได้:

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

กฎสำหรับแทร็กคาร์เรซที่ถูกต้องมีดังนี้:

  • ไม่มีถนนที่ไปไหนเลย
  • มันจะต้องเป็นวง (และชิ้นส่วนทั้งหมดจะต้องเป็นส่วนหนึ่งของวงเดียวกัน)
  • ที่สะพาน / ทางลอดคุณไม่สามารถเลี้ยว (ดังนั้นคุณจะต้องผ่านมันไป)

น่าเสียดายที่ลู่วิ่งของรถแข่งทำให้หลานสาวของฉันและฉันมีจำนวน จำกัด แต่เราต้องการใช้มันทั้งหมดในการติดตามอย่างแน่นอน เขียนโปรแกรมที่ให้รายชื่อชิ้นส่วนที่อยู่ในรายการสินค้าของเราเอาท์พุทแทร็กคาร์เรซที่ใช้ชิ้นส่วนเหล่านั้นทั้งหมด

ป้อนคำอธิบาย

เราต้องการให้อินพุตเข้ามาทาง 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 "ในทางทฤษฎี" คุณให้ความหมายอะไรกับผลลัพธ์ คุณหมายถึงโปรแกรมที่จะไม่ยุติการทำงานเป็นเวลานาน แต่จะให้ผลลัพธ์หรือไม่?
absinthe

1
หนึ่งอาจจะสามารถสร้างเขตข้อมูลของสี่เหลี่ยมจัตุรัส nxn ที่เต็มไปด้วยชิ้นส่วนการแข่งขันและสี่เหลี่ยมว่างเปล่าที่คุณสามารถสร้างการเรียงสับเปลี่ยนจนกว่าคุณจะพบสิ่งที่เป็นสนามแข่ง ที่จะใช้เวลาตลอดไปเพื่ออะไรมากกว่าสองสามแผ่น
Sumurai8

4
@ Sumurai8 โอเคฉันเข้าใจแล้ว ฉันต้องการโปรแกรมที่จะให้ผลลัพธ์ก่อนที่ heat heat ของเอกภพสำหรับอินพุตค่าเล็ก ๆ ที่ฉันได้แสดงในการท้าทาย
absinthe

4
หลานสาวของคุณไม่อดทนพอ! : P
Sumurai8

คำตอบ:


4

Ruby 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))
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.