Pilotu kurtar!


12

Soru

Uzay istasyonlarında robot ordusu tarafından yakalandık. Uzay gemisi pilotumuz 1. seviyede olan hapishanede. Kaçmanın tek bir yolu var ve uzay gemisi pilotunu kurtarmak. Bu, seviye N'den seviye 1'e geçmek anlamına geliyor. Ancak çok riskli olduğu için, mümkün olan en az sayıda adımda hapishaneye gitmeniz gerekiyor.

Koşullar

  • Taşınmanın 4 yolu vardır:

    1. Seviye N'den Seviye N - 1'e geçin e.g. from 12 to 11
    2. Seviye N'den Seviye N + 1'e geçin e.g. from 12 to 13
    3. Seviye 2k'den k seviyesine ışınlanma kullanın e.g. from 12 to 6
    4. Seviye 3k'den k seviyesine ışınlanma kullanın e.g. from 12 to 4
  • Işınlanmalar tek yönlüdür (12'den 4'e kadar alabilirsiniz, ancak 4'den 12'ye kadar almak imkansızdır)

  • Her eylem bir adım alır

Giriş

Giriş STDIN'den veya programlama dilinizdeki en yakın alternatiften okunmalıdır. Girdi nburada bir tamsayıdan oluşur 1 <= n <= 10^8.

Çıktı

Çıktı, nseviye 1'e gitmek için gereken minimum adım sayısı olmalıdır .

Örnekler

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

Uzay gemisi pilotumuzu en kısa sürede hapishaneden kurtarmamıza ve eve dönmemize yardımcı olacak programı kodlamaya çalışın!

En kısa kod kazanacak!


7
Bir cevabı kabul etmeden önce en az bir hafta beklemek tavsiye edilir (ancak zorunlu değildir). Gelecekte daha kısa bir cevap gönderilirse kabul edilen yanıtı değiştirmek isteseniz bile, diğerleri bu yarışmanın "sona erdiği" izlenimine katılabilir ve katılmaktan kaçınabilir.
Dennis

1
Bu meydan okuma bana hesap makinemle oynadığım bir oyunu hatırlatıyor: Ekranı dolduran ve 2, 3 veya 5'e bölmeye çalışabildiğim kadar bir sayı yazardım, sonra 1 ekleyip çıkartıp devam ediyorum.
Arcturus

Yanıtlar:


8

Pyth, 32 bayt

L?<b3tbhSsmm+h%_Wkbdy/+kbd2tS3yQ

Çevrimiçi deneyin: Gösteri veya Test Paketi

Açıklama:

Sorunu biraz değiştirdim. Sorunun 4 işleminin yerine 4 yeni işlem tanımlarım.

  • level / 2( (level % 2) + 1adım olarak sayılır , çünkü ışınlanabilmek için önce bir düzey aşağı gitmeniz gerekebilir)
  • (level + 1) / 2( (level % 2) + 1adım olarak sayılır )
  • level / 3( (level % 3) + 1adım olarak sayılır )
  • (level + 1) / 3( (-level % 3) + 1adım olarak sayılır )

Tasarım gereği bu işlemler sayı ise, her bir numaraya uygulanabilir 0 mod 2, 1 mod 2, 0 mod 3, 1 mod 3ya da 2 mod 3.

Bunun neden işe yaradığını kolayca düşünebilirsiniz. Ana fikir, arka arkaya iki (aşağı hareket) veya iki (yukarı hareket) hareketi olmayan en az bir optimal çözümün olmasıdır. Kanıt: Bir satırda böyle iki hareket içeren bir çözümünüz varsa, bunları değiştirebilir ve çözümü daha küçük veya eşit uzunlukta yapabilirsiniz. Örneğin, diğer tüm durumlarda (yukarı taşı, yukarı taşı, yukarı taşı, 2k'den k'ye ışınlanma) ((2k'den k'ya ışınlanma, yukarı taşı) ve benzerlerini değiştirebilirsiniz.

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

İşlev yörtülü olarak not kullanır ve bu nedenle çalışma zamanı patlamaz.


1
Ana fikir, en uygun çözümde arka arkaya asla iki (aşağı hareket) ya da iki (yukarı hareket) hareket olmamasıdır. - Peki ya 29 -> 28 -> 27 -> 9 -> 3 -> 1? Bu en uygun çözümse, iki-yukarı / iki-aşağı yoluna her zaman bir alternatif olduğuna nasıl karar verdiniz, bu da daha garip bir sayı bölgesine yol açmadı?
TessellatingHeckler

1
@TessellatingHeckler Belki daha kesin olmalıydım. Orada , en az bir uygun çözüm, yani üst üste iki (aşağı hareket) ya da iki (yukarı hareket) hareketleri bulunmamaktadır. Ör 29 -> 30 -> 10 -> 9 -> 3 -> 1
Jakube

Bunun yanlış olduğunu söylemiyorum, sadece "neden işe yaradığını kolayca düşünemiyorum". Akıl yürütüyordum: 1. odaya giden en hızlı rota üç güçte başlıyor, her harekette üçe bölünüyor. Böylece, üç güce yakın sayılar için en hızlı rota, üç güce ulaşmak için tekrarlanan çıkarma veya toplamadır, daha sonra tekrar tekrar 3'e bölün. Bunun yerine, n / 2'yi hareket ettirerek başlarlarsa, üçün gücünden uzaklaşırlar, ve bu nedenle mümkün olan en hızlı rotayı geçmiş. Eşit derecede kısa başka bir rota nasıl / her zaman / bulacaklarını görmüyorum; şu anda 'daha kötü' seçimleri olan bir bölgede oldukları anlaşılıyor.
TessellatingHeckler

4

Python, 176 bayt

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]

Kaba kuvvet tüm yol boyunca; 1 to 100,000,00064 bit bilgisayardaki tüm numaraların listesi ~ 800Mb bellektir.

Liste dizini sayıları, değerler izin verilen kurtarma adımlarında 1 ile olan mesafeyi temsil eder.

  • "1 adımda ulaşılabilir" anlamına gelen liste [1] = 0 olarak ayarlayın.
  • listedeki 0 adımda ulaşılabilen her sayı için (yani 1)
    • set numarası + 1, sayı-1, sayı * 2, sayı * 3 1 adımda ulaşılabilir
  • listedeki 1 adımda ulaşılabilen her sayı için (yani 0,2,2,3)
    • set numarası + 1, sayı-1, sayı * 2, sayı * 3 2 adımda ulaşılabilir
  • ... vb. her liste dizinine ulaşana kadar.

Çalışma süresi 10 dakikadan biraz fazla. * Ehem *.

Kod yorumları

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.

Diğer

  • PythonWin'de çalıştırırsanız, daha sonra yorumlayıcıdaki L listesine erişebilirsiniz.
  • Her odanın kaptanına 30 hamle veya daha az sürede girmesi gerekir.
  • Sadece bir oda 30 hamle uzaklıktadır - oda 72,559,411 - ve 29 hamle olan 244 oda bulunmaktadır.
  • Maksimum vaka için korkunç çalışma zamanı özelliklerine sahip olabilir, ancak soru yorumlarından biri " @ 5 dakikada 20000 test vakası için en kısa yolları bulması gereken tüm programları bulur " ve <6 saniyede 1-20.001 test eder.

2

Python 2 ... 1050

kötü yazılmış, dinsiz, uygunsuz.

Stdin'de başlangıç ​​seviyesini okur, stdout'ta minimum adım sayısını yazdırır.

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 bit x86 makine kodu, 59 bayt

Onaltılı olarak:

31c9487435405031d231dbb103f7f14a7c070f94c301d008d353e8e3ffffff5b00c3585331d2b102f7f152e8d2ffffff5a00d05a38d076019240c3

Makine dili se başına standart girdi kavramı yok. Zor saf hesaplama olduğu için, girdi parametresini alan EAXve sonucu döndüren bir işlev yazmayı seçtim AL.

Kodun arkasındaki matematik @Jakube tarafından güzel bir şekilde açıklanmıştır: arama sadece ışınlanmaların birden fazla tek seviyeli hareketle serpiştirildiği yollar arasında gerçekleştirilir. Performans, giriş aralığının alt ucunda saniyede yaklaşık 12000 test ve üst uçta saniyede 50 durumdur. Bellek tüketimi, özyineleme düzeyi başına 12 bayt yığın alanıdır.

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           
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.