ช่วยนักบิน!


12

คำถาม

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

เงื่อนไข

  • มี 4 วิธีในการเคลื่อนย้าย:

    1. ย้ายจากระดับ N ถึงระดับ N - 1 e.g. from 12 to 11
    2. ย้ายจากระดับ N เป็นระดับ N + 1 e.g. from 12 to 13
    3. ใช้ teleport จากระดับ 2k ถึงระดับ k e.g. from 12 to 6
    4. ใช้ teleport จากระดับ 3k ถึงระดับ k e.g. from 12 to 4
  • เทเลพอร์ตมีเพียงเที่ยวเดียว (คุณสามารถได้รับ 12 ถึง 4 แต่เป็นไปไม่ได้ที่จะได้รับ 4 ถึง 12)

  • ทุกการกระทำใช้ขั้นตอนเดียว

อินพุต

อินพุตควรอ่านจาก STDIN หรือทางเลือกที่ใกล้เคียงที่สุดในภาษาการเขียนโปรแกรมของคุณ การป้อนข้อมูลประกอบด้วยจำนวนเต็มที่n1 <= n <= 10^8

เอาท์พุต

ผลลัพธ์ควรเป็นจำนวนขั้นต่ำในการไปจากnระดับ 1

ตัวอย่าง

Level         Minimum number of steps
1             0
8             3
10            3
267           7
100,000,000   25

ลองโปรแกรมรหัสที่จะช่วยให้เราช่วยนักบินอวกาศยานอวกาศของเราออกจากคุกในเวลาอันสั้นและกลับบ้าน!

รหัสที่สั้นที่สุดจะเป็นผู้ชนะ!


7
ขอแนะนำ (แต่ไม่บังคับ) ให้รออย่างน้อยหนึ่งสัปดาห์ก่อนที่จะตอบคำถาม แม้ว่าคุณต้องการเปลี่ยนคำตอบที่ยอมรับได้หากคำตอบที่สั้นกว่านั้นถูกโพสต์ในอนาคตผู้อื่นอาจได้รับความประทับใจว่าการแข่งขันนี้ "เกิน" และละเว้นจากการเข้าร่วม
เดนนิส

1
ความท้าทายนี้ทำให้ฉันนึกถึงเกมที่ฉันเคยเล่นกับเครื่องคิดเลข: ฉันจะพิมพ์ตัวเลขที่เติมหน้าจอและพยายามหารด้วย 2, 3 หรือ 5 ให้มากที่สุดเท่าที่จะทำได้จากนั้นเพิ่ม / ลบ 1 และดำเนินการต่อ
Arcturus

คำตอบ:


8

Pyth, 32 ไบต์

L?<b3tbhSsmm+h%_Wkbdy/+kbd2tS3yQ

ลองใช้ออนไลน์: การสาธิตหรือชุดทดสอบ

คำอธิบาย:

ฉันเปลี่ยนปัญหาเล็กน้อย ฉันกำหนดการดำเนินงานใหม่ 4 รายการซึ่งแทนที่การดำเนินการ 4 ข้อของคำถาม

  • level / 2(นับเป็น(level % 2) + 1ขั้นตอนเนื่องจากคุณอาจต้องเลื่อนระดับลงก่อนเพื่อส่งผ่านทางไกล)
  • (level + 1) / 2(นับเป็น(level % 2) + 1ขั้นตอน)
  • level / 3(นับเป็น(level % 3) + 1ขั้นตอน)
  • (level + 1) / 3(นับเป็น(-level % 3) + 1ขั้นตอน)

โดยการออกแบบการดำเนินงานเหล่านี้สามารถนำไปใช้กับแต่ละหมายเลขถ้าตัวเลขคือ0 mod 2, 1 mod 2, 0 mod 3, หรือ1 mod 32 mod 3

คุณสามารถคิดได้อย่างง่ายดายว่าทำไมมันถึงได้ผล แนวคิดหลักคือมีทางออกที่ดีที่สุดอย่างน้อยหนึ่งอย่างที่ไม่มีการเคลื่อนไหวสองแบบ (เลื่อนลง) หรือเลื่อนขึ้นสองครั้งในหนึ่งแถว พิสูจน์: ถ้าคุณมีวิธีแก้ปัญหานั่นมีการเคลื่อนไหวสองอย่างต่อเนื่องกันกว่าที่คุณสามารถแทนที่พวกมันและทำให้โซลูชันมีขนาดเล็กลงหรือยาวเท่ากัน ตัวอย่างเช่นคุณสามารถแทนที่ (เลื่อนขึ้นเลื่อนขึ้นเคลื่อนย้ายจาก 2k เป็น k) โดย (เคลื่อนย้ายจาก 2k เป็น k เลื่อนขึ้น) และคล้ายคลึงกันในทุกกรณี

L?<b3tbhSsmm+h%_Wkbdy/+kbd2tS3yQ
L                                 define a function y(b), which returns:
 ?<b3                               if b < 3:
     tb                               return b-1
                                    else:
          m                tS3        map each d in [2, 3] to:
           m              2             map each k in [0, 1] to:
              %_Wkbd                      (b mod d) if k == 0 else (-b mod d)
             h                            +1, this gives the additional steps
            +       y/+kbd                + f((b+k)/d) (recursive call)
         s                            combine the two lists
       hS                             and return the minimum element
                               yQ call y with input number

ฟังก์ชั่นyใช้การบันทึกความจำโดยปริยายดังนั้นรันไทม์จะไม่ระเบิด


1
แนวคิดหลักคือการเคลื่อนไหวไม่เคยสองครั้ง (เลื่อนลง) หรือสองครั้ง (เลื่อนขึ้น) ในแถวในโซลูชันที่ดีที่สุด - แล้ว 29 -> 28 -> 27 -> 9 -> 3 -> 1? หากนั่นเป็นทางออกที่ดีที่สุดคุณตัดสินใจได้อย่างไรว่ามีทางเลือกอื่นในเส้นทางสองทางขึ้น / ลงสองทางซึ่งไม่นำไปสู่ภูมิภาคที่มีตัวเลขที่น่าอึดอัดใจมากขึ้น?
TessellatingHeckler

1
@TessellatingHeckler บางทีฉันควรจะแม่นยำมากขึ้น มีทางออกที่ดีที่สุดอย่างน้อยหนึ่งวิธีที่ไม่มีการเคลื่อนไหวสองแบบ (เลื่อนลง) หรือสองครั้ง (เลื่อนขึ้น) ในแถว เช่น 29 -> 30 -> 10 -> 9 -> 3 -> 1
Jakube

ฉันไม่ได้พูดว่ามันผิด แต่ฉันไม่สามารถ "คิดได้อย่างง่ายดายว่าทำไมมันถึงได้ผล" ฉันใช้เหตุผล: เส้นทางที่เร็วที่สุดไปยังห้องที่ 1 เริ่มต้นด้วยพลังของสามแยกสามต่อทุก ดังนั้นเส้นทางที่เร็วที่สุดสำหรับตัวเลขที่อยู่ใกล้กับกำลังสามคือการลบซ้ำ ๆ หรือบวกเข้ากับกำลังสามแล้วหารซ้ำด้วย 3 หากพวกเขาเริ่มต้นด้วยการเลื่อนการเคลื่อนที่ n / 2 พวกเขาจะได้รับเพิ่มเติมจากกำลังสาม และดังนั้นจึงผ่านเส้นทางที่เร็วที่สุดที่เป็นไปได้ ฉันไม่เห็นว่าพวกเขาจะ / เสมอ / ค้นหาเส้นทางอื่นที่สั้นพอ ๆ กัน; ดูเหมือนว่าพวกเขาจะอยู่ในภูมิภาคที่มีตัวเลือก 'แย่' ในขณะนี้
TessellatingHeckler

4

Python ขนาด 176 ไบต์

n=int(1e8);s,f,L=0,input(),[1,0]+[n]*(n-1)
def h(q):
 if 0<=q<=n and L[q]>s:L[q]=s+1
while n in L:
 for i,v in enumerate(L):
  if v==s:map(h,(i-1,i+1,i*2,i*3))
 s+=1
print L[f]

กำลังดุร้ายอยู่ตลอดทาง; รายการตัวเลขทั้งหมด1 to 100,000,000ในคอมพิวเตอร์ 64 บิตคือหน่วยความจำ ~ 800Mb

ดัชนีรายการแสดงถึงตัวเลขค่าแสดงระยะทางจาก 1 ในขั้นตอนการช่วยเหลือที่ได้รับอนุญาต

  • Set list [1] = 0 ความหมาย "เข้าถึงได้ใน 0 ขั้นตอน"
  • สำหรับทุกหมายเลขในรายการที่สามารถเข้าถึงได้ใน 0 ขั้นตอน (เช่น1)
    • set number + 1, number-1, number * 2, number * 3 สามารถเข้าถึงได้ใน 1 ขั้นตอน
  • สำหรับทุกหมายเลขในรายการที่สามารถเข้าถึงได้ใน 1 ขั้นตอน (เช่น0,2,2,3)
    • กำหนดหมายเลข + 1, หมายเลข 1, หมายเลข * 2, หมายเลข * 3 สามารถเข้าถึงได้ใน 2 ขั้นตอน
  • ... เป็นต้นจนกว่าจะถึงทุกรายการดัชนี

เวลาของรันไทม์นานกว่า 10 นาที * * * * * * * * อะแฮ่ม

ความคิดเห็นรหัส

n=int(1e8)           # max input limit.
s=0                  # tracks moves from 1 to a given number.
f=input()            # user input.

L=[1,0]+[n]*(n-1)    # A list where 0 can get to room 1 in 1 step,
                     # 1 can get to itself in 0 steps,
                     # all other rooms can get to room 1 in 
                     # max-input-limit steps as a huge upper bound.


def helper(q):
    if 0<=q<=n:      # Don't exceed the list boundaries.
      if L[q]>s:     # If we've already got to this room in fewer steps
                     # don't update it with a longer path.
          L[q]=s+1   # Can get between rooms 1 and q in steps+1 actions.


while n in L:        # until there are no values still at the 
                     # original huge upper bound

 for i,v in enumerate(L):
  if v==s:                            # only pick out list items
                                      # rechable in current s steps,
      map(helper,(i-1,i+1,i*2,i*3))   # and set the next ones reachable
                                      # in s+1 steps.

 s+=1                                 # Go through the list again to find
                                      # rooms reachable in s+1 steps

print L[f]                            # show answer to the user.

อื่น ๆ

  • หากคุณรันใน PythonWin คุณสามารถเข้าถึงรายการ L ในล่ามหลังจากนั้น
  • ห้องพักทุกห้องมีเส้นทางไปยังกัปตันใน 30 เคลื่อนไหวหรือน้อยกว่า
  • มีเพียงหนึ่งห้องเท่านั้นที่เคลื่อนที่ไปได้ 30 ห้อง - ห้อง 72,559,411 - และมี 244 ห้องที่ย้ายออกไป 29 ห้อง
  • มันอาจมีลักษณะรันไทม์ที่แย่มากสำหรับกรณีสูงสุด แต่หนึ่งในความคิดเห็นของคำถามคือ " @Geobits โปรแกรมทั้งหมดที่ควรหาวิธีที่สั้นที่สุดสำหรับกรณีทดสอบ 20000 ใน 5 นาที " และทดสอบ 1-20,001 ใน <6 วินาที

2

Python 2 ... 1,050

เขียนได้ไม่ดีไม่อัปโหลดไม่ดี

อ่านระดับเริ่มต้นของ stdin พิมพ์จำนวนขั้นต่ำใน stdout

def neighbors(l):
    yield l+1
    yield l-1
    if not l%3:yield l/3
    if not l%2:yield l/2

def findpath(start, goal):
    closedset = set([])
    openset = set([start])
    came_from = {}
    g_score = {}
    g_score[start] = 0
    f_score = {}
    f_score[start] = 1
    while len(openset) > 0:
        current = min(f_score, key=f_score.get)
        if current == goal:
            return came_from
        else:
            openset.discard(current)
        del f_score[current]
        closedset.add(current)
        for neighbor in neighbors(current):
            if neighbor in closedset:
                continue
            tentative_g_score = g_score[current] + 1
            if (neighbor not in openset) or (tentative_g_score < g_score[neighbor]):
                came_from[neighbor] = current
                g_score[neighbor] = tentative_g_score
                f_score[neighbor] = tentative_g_score + 1
                openset.add(neighbor)
def min_path(n):
    path = findpath(n,1)
    i,current = 0,1
    while current <> n:
        i+=1
        current = path[current]
    return i
print min_path(input())

2

รหัสเครื่อง 32 บิต x86 ขนาด 59 ไบต์

ในรูปหกเหลี่ยม:

31c9487435405031d231dbb103f7f14a7c070f94c301d008d353e8e3ffffff5b00c3585331d2b102f7f152e8d2ffffff5a00d05a38d076019240c3

ภาษาเครื่องต่อ seไม่มีแนวคิดของอินพุตมาตรฐาน ตั้งแต่ความท้าทายคือการคำนวณบริสุทธิ์ฉันเลือกที่จะเขียนฟังก์ชั่นการใช้พารามิเตอร์สำหรับการป้อนในและผลที่กลับมาในEAXAL

คณิตศาสตร์ที่อยู่เบื้องหลังรหัสได้รับการอธิบายอย่างดีโดย @Jakube: การค้นหาจะดำเนินการเฉพาะในเส้นทางที่มีเทเลพอร์ตเคลื่อนย้ายโดยไม่มีการเคลื่อนไหวหนึ่งระดับ ประสิทธิภาพอยู่ที่ประมาณ 12,000 กรณีทดสอบต่อวินาทีในช่วงล่างสุดของช่วงอินพุตและ 50 กรณีต่อวินาทีในตอนท้าย การใช้หน่วยความจำคือ 12 ไบต์ของพื้นที่สแต็กต่อระดับการเรียกซ้ำ

0:  31 c9               xor ecx, ecx  
_proc:
2:  48                  dec eax       
3:  74 35               je _ret       ;if (EAX==1) return 0;
5:  40                  inc eax       ;Restore EAX
6:  50                  push eax      
7:  31 d2               xor edx, edx  ;Prepare EDX for 'div'
9:  31 db               xor ebx, ebx  
b:  b1 03               mov cl, 3     
d:  f7 f1               div ecx       ;EAX=int(EAX/3); EDX=EAX%3
f:  4a                  dec edx       ;EDX is [-1..1]
10: 7c 07               jl _div3      ;EDX<0 (i.e. EAX%3==0)
12: 0f 94 c3            sete bl       ;BL=EDX==0?1:0
15: 01 d0               add eax, edx  ;If EAX%3==2, we'd go +1 level before teleport
17: 08 d3               or bl, dl     ;BL=EAX%3!=0
_div3:
19: 53                  push ebx      ;Save register before...
1a: e8 e3 ff ff ff      call _proc    ;...recursive call
1f: 5b                  pop ebx       
20: 00 c3               add bl, al    ;BL is now # of steps if taken 3n->n route (adjusted) less one
22: 58                  pop eax       ;Restore original input parameter's value
23: 53                  push ebx      
24: 31 d2               xor edx, edx  
26: b1 02               mov cl, 2     
28: f7 f1               div ecx       ;EAX=EAX>>1; EDX=EAX%2
2a: 52                  push edx      ;Save register before...
2b: e8 d2 ff ff ff      call _proc    ;...another recursive call
30: 5a                  pop edx       
31: 00 d0               add al, dl    ;AL is now # of steps if using 2n->n route (adjusted) less one
33: 5a                  pop edx       
34: 38 d0               cmp al, dl    ;Compare two routes
36: 76 01               jbe _nsw      
38: 92                  xchg eax, edx ;EAX=min(EAX,EDX)
_nsw:
39: 40                  inc eax       ;Add one for the teleport itself
_ret:
3a: c3                  ret           
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.