パイロットを救え!


12

質問

私たちは彼らの宇宙ステーションでロボット軍に捕らえられています。私たちの宇宙船パイロットはレベル1の刑務所にいます。脱出する方法は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. レベル2kからレベルkまでテレポートを使用する e.g. from 12 to 6
    4. レベル3kからレベルkまでテレポートを使用する e.g. from 12 to 4
  • テレポートは一方向のみです(12から4まで取得できますが、4から12まで取得することはできません)

  • すべてのアクションは一歩を踏み出します

入力

入力は、STDIN、またはプログラミング言語の最も近い代替から読み取る必要があります。入力は、整数nで構成され1 <= n <= 10^8ます。

出力

出力は、nレベル1 に進むために必要な最小ステップ数でなければなりません。

例

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

最短時間で宇宙船パイロットを刑務所から救い、家に帰るのを助けるプログラムをコーディングしてみてください!

最短のコードが勝ちます!


7
回答を受け入れる前に少なくとも1週間待つことをお勧めします(ただし必須ではありません)。将来短い回答が投稿された場​​合に受け入れられた回答を変更するつもりであっても、他の人はこのコンテストが「終わった」という印象を受け、参加を控えることがあります。
— デニス

1
このチャレンジは、電卓でプレイしていたゲームを思い出させます。画面をいっぱいにする数字を入力し、できる限り2、3、または5で除算してから、1を加算/減算して続行します。
— アークトゥルス

回答:


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 3または2 mod 3。

これがなぜ機能するのか、簡単に考えることができます。主なアイデアは、少なくとも1つの最適なソリューションがあり、2つの(下に移動する)または2つの(上に移動する)モーションが連続しないことです。証明:そのようなモーションが2つ連続して存在するソリューションがある場合、それらを置き換えて、ソリューションの長さを短くしたり、等しくしたりすることができます。たとえば、(2kからkへのテレポート)を(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
主なアイデアは、最適なソリューションでは、2つの運動(下に移動)または2つの運動(上に移動)が連続しないことです。-29-> 28-> 27-> 9-> 3-> 1はどうですか?それが最適なソリューションである場合、2アップ/ 2ダウンパスの代替手段が常に存在することをどのように判断しましたか?
— TessellatingHeckler

1
@TessellatingHecklerたぶんもっと正確にすべきだった。少なくとも 2つの(下に移動する)または2つの(上に移動する)モーションを持たない最適なソリューションが少なくとも1つあります。例:
— 29-

私はそれが間違っているとは言わず、「なぜそれが機能するのかを簡単に考えることができない」だけだ。部屋1への最速のルートは3の累乗から始まり、1移動ごとに3で除算します。したがって、3の累乗に近い数の最速ルートは、3の累乗になるまで減算または加算を繰り返し、3で繰り返し除算します。したがって、可能な限り最速のルートを通過します。私は彼らがどのように/常に/同様に短い別のルートを見つけるかわかりません。彼らは今、「悪い」選択のある地域にいるようです。
— 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,00064ビットコンピューター上のすべての数値のリストは、〜800Mbのメモリです。

リストインデックスは数値を表し、値は許可された救助手順の1からの距離を表します。

  • 「0ステップで到達可能」を意味するlist [1] = 0を設定します。
  • 0ステップで到達可能なリスト内のすべての番号(つまり1)
    • 1ステップで到達可能なnumber + 1、number-1、number * 2、number * 3を設定します
  • 1ステップで到達可能なリスト内のすべての番号(つまり0,2,2,3)
    • 2つのステップで到達可能なnumber + 1、number-1、number * 2、number * 3を設定します
  • ...など。すべてのリストインデックスに達するまで。

ランタイムは10分を少し超えています。* ahem *。

コードコメント

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回以内にキャプテンへのパスがあります。
  • ルーム72,559,411の1部屋のみが30の移動先で、29の移動先の244の部屋があります。
  • 最大のケースではひどいランタイム特性を持っているかもしれませんが、質問コメントの1つは「5分間で20000個のテストケースの最短方法を見つける必要があるすべてのプログラム」で、6秒未満で1-20,001をテストします。

2

Python 2 ... 1050

不十分に書かれた、手に負えない、最適ではない。

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バイト

16進数で:

31c9487435405031d231dbb103f7f14a7c070f94c301d008d353e8e3ffffff5b00c3585331d2b102f7f152e8d2ffffff5a00d05a38d076019240c3

機械語自体には、標準入力の概念はありません。課題は純粋な計算であるため、入力パラメーターを取りEAX、結果を返す関数を作成することにしましたAL。

コードの背後にある数学は、@ Jakubeによってうまく説明されています。検索は、1レベル以下の移動で散らばったテレポートを持つパス間でのみ実行されます。パフォーマンスは、入力範囲の下限で毎秒約12000テストケース、上限で毎秒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.