Cứu phi công!


12

Câu hỏi

Chúng tôi bị quân đội robot bắt giữ trên trạm vũ trụ của họ. Phi công tàu không gian của chúng tôi đang ở trong nhà tù ở cấp 1. Chỉ có một cách để trốn thoát và đó là giải cứu phi công tàu không gian của bạn. Điều đó có nghĩa là chuyển từ cấp N sang cấp 1. Tuy nhiên, vì rất nguy hiểm, bạn cần đến nhà tù với số bước ít nhất có thể.

Điều kiện

  • Có 4 cách để di chuyển:

    1. Chuyển từ cấp N sang cấp N - 1 e.g. from 12 to 11
    2. Chuyển từ cấp N sang cấp N + 1 e.g. from 12 to 13
    3. Sử dụng dịch chuyển tức thời từ cấp 2k đến cấp k e.g. from 12 to 6
    4. Sử dụng dịch chuyển tức thời từ cấp 3k đến cấp k e.g. from 12 to 4
  • Dịch chuyển tức thời chỉ là một chiều (bạn có thể nhận được từ 12 đến 4 nhưng không thể nhận được từ 4 đến 12)

  • Mỗi hành động mất một bước

Đầu vào

Đầu vào phải được đọc từ STDIN hoặc thay thế gần nhất trong ngôn ngữ lập trình của bạn. Đầu vào bao gồm một số nguyên ntrong đó 1 <= n <= 10^8.

Đầu ra

Đầu ra phải là số bước tối thiểu cần thiết để chuyển từ ncấp 1.

Ví dụ

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

Hãy thử mã chương trình sẽ giúp chúng tôi cứu phi công tàu không gian của chúng tôi khỏi nhà tù trong thời gian ngắn nhất và trở về nhà!

Mã ngắn nhất sẽ giành chiến thắng!


7
Đó là khuyến khích (nhưng không bắt buộc) phải đợi ít nhất một tuần trước khi chấp nhận câu trả lời. Ngay cả khi bạn có ý định thay đổi câu trả lời được chấp nhận nếu một câu trả lời ngắn hơn được đăng trong tương lai, những người khác có thể có ấn tượng rằng cuộc thi này đã "kết thúc" và không tham gia.
Dennis

1
Thử thách này làm tôi nhớ đến một trò chơi tôi từng chơi với máy tính của mình: Tôi sẽ gõ một số lấp đầy màn hình và cố gắng chia cho 2, 3 hoặc 5 nhiều nhất có thể, sau đó thêm / trừ 1 và tiếp tục.
Arcturus

Câu trả lời:


8

Bình thường, 32 byte

L?<b3tbhSsmm+h%_Wkbdy/+kbd2tS3yQ

Dùng thử trực tuyến: Trình diễn hoặc Test Suite

Giải trình:

Tôi đã chuyển đổi vấn đề một chút. Tôi xác định 4 thao tác mới, thay thế 4 thao tác của câu hỏi.

  • level / 2(được tính là (level % 2) + 1các bước, vì trước tiên bạn có thể cần phải di chuyển xuống một cấp để dịch chuyển tức thời)
  • (level + 1) / 2(được tính là (level % 2) + 1các bước)
  • level / 3(được tính là (level % 3) + 1các bước)
  • (level + 1) / 3(được tính là (-level % 3) + 1các bước)

Theo thiết kế các hoạt động này có thể được áp dụng cho mỗi số, nếu số là 0 mod 2, 1 mod 2, 0 mod 3, 1 mod 3hoặc 2 mod 3.

Bạn có thể dễ dàng nghĩ về, tại sao điều này hoạt động. Ý tưởng chính là, có ít nhất một giải pháp tối ưu, không có hai chuyển động (di chuyển xuống) hoặc hai (di chuyển lên) liên tiếp. Bằng chứng: Nếu bạn có một giải pháp, có hai chuyển động như vậy liên tiếp, bạn có thể thay thế chúng và làm cho giải pháp nhỏ hơn hoặc bằng chiều dài. Chẳng hạn, bạn có thể thay thế (di chuyển lên, di chuyển lên, dịch chuyển tức thời từ 2k đến k) bằng cách (dịch chuyển từ 2k đến k, di chuyển lên) và tương tự trong tất cả các trường hợp khác.

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

Hàm ysử dụng ghi nhớ ngầm và do đó thời gian chạy không phát nổ.


1
Ý tưởng chính là, không bao giờ có hai chuyển động (di chuyển xuống) hoặc hai (di chuyển lên) liên tiếp trong giải pháp tối ưu. - còn 29 -> 28 -> 27 -> 9 -> 3 -> 1 thì sao? Nếu đó là một giải pháp tối ưu, làm thế nào bạn quyết định rằng luôn có một giải pháp thay thế cho con đường hai lên / hai xuống, mà không dẫn đến một khu vực số khó xử hơn?
TessellatingHeckler

1
@TessellatingHeckler Có lẽ tôi nên chính xác hơn. Có ít nhất một giải pháp tối ưu, không có hai chuyển động (di chuyển xuống) hoặc hai (di chuyển lên) liên tiếp. Ví dụ: 29 -> 30 -> 10 -> 9 -> 3 -> 1
Jakube

Tôi không nói rằng nó sai, chỉ là tôi không thể "dễ dàng nghĩ về lý do tại sao nó hoạt động". Tôi đã suy luận: con đường nhanh nhất đến phòng 1 đang bắt đầu với sức mạnh ba, chia cho ba mỗi lần di chuyển. Vì vậy, con đường nhanh nhất cho các số gần lũy thừa ba là phép trừ hoặc phép cộng lặp lại để có được sức mạnh của ba, sau đó chia liên tục cho 3. Nếu thay vào đó, chúng bắt đầu bằng cách di chuyển n / 2, chúng sẽ nhận được nhiều hơn từ sức mạnh của ba, và do đó vượt qua con đường nhanh nhất có thể. Tôi không thấy họ sẽ / luôn luôn / tìm một tuyến đường khác ngắn như nhau; có vẻ như bây giờ họ đang ở trong một khu vực có những lựa chọn 'tệ hơn'.
TessellatingHeckler

4

Python, 176 byte

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]

Lực lượng vũ phu tất cả các cách; một danh sách tất cả các số 1 to 100,000,000trên máy tính 64 bit là ~ 800Mb bộ nhớ.

Chỉ số danh sách đại diện cho các số, giá trị biểu thị khoảng cách từ 1 trong các bước cứu hộ được phép.

  • Đặt danh sách [1] = 0 có nghĩa là "có thể truy cập trong 0 bước".
  • cho mỗi số trong danh sách có thể truy cập trong 0 bước (nghĩa là 1)
    • đặt số + 1, số-1, số * 2, số * 3 có thể truy cập trong 1 bước
  • cho mỗi số trong danh sách có thể truy cập trong 1 bước (nghĩa là 0,2,2,3)
    • đặt số + 1, số-1, số * 2, số * 3 có thể truy cập trong 2 bước
  • ... vv cho đến khi đạt được mọi chỉ số danh sách.

Thời gian chạy là một chút hơn 10 phút. * ahem *.

Mã ý kiến

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.

Khác

  • Nếu bạn chạy nó trong PythonWin, bạn có thể truy cập danh sách L trong trình thông dịch sau đó.
  • Mỗi phòng có một đường dẫn đến thuyền trưởng trong 30 lần di chuyển hoặc ít hơn.
  • Chỉ có một phòng là 30 di chuyển - phòng 72,559,411 - và có 244 phòng cách đó 29 di chuyển.
  • Nó có thể có các đặc điểm thời gian chạy khủng khiếp cho trường hợp tối đa, nhưng một trong những ý kiến ​​câu hỏi là " @Geobits tất cả các chương trình sẽ tìm cách ngắn nhất cho 20000 trường hợp thử nghiệm trong 5 phút " và nó kiểm tra 1-20.001 trong <6 giây.

2

Con trăn 2 ... 1050

viết kém, vô văn hóa, không tối ưu.

Đọc mức bắt đầu trên stdin, in số bước tối thiểu trên thiết bị xuất chuẩn.

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

Mã máy 32 bit x86, 59 byte

Trong hex:

31c9487435405031d231dbb103f7f14a7c070f94c301d008d353e8e3ffffff5b00c3585331d2b102f7f152e8d2ffffff5a00d05a38d076019240c3

Ngôn ngữ máy mỗi se không có khái niệm về đầu vào tiêu chuẩn. Vì thử thách là tính toán thuần túy, tôi đã chọn viết một hàm lấy tham số đầu vào EAXvà trả về kết quả AL.

Toán học đằng sau mã được giải thích độc đáo bởi @Jakube: việc tìm kiếm chỉ được tiến hành trong số các đường dẫn có dịch chuyển tức thời xen kẽ với không quá một lần di chuyển một cấp. Hiệu suất là khoảng 12000 trường hợp thử nghiệm mỗi giây ở đầu dưới của phạm vi đầu vào và 50 trường hợp mỗi giây ở đầu trên. Tiêu thụ bộ nhớ là 12 byte không gian ngăn xếp cho mỗi mức đệ quy.

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           
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.