救救飞行员!


12

我们被他们的空间站上的机器人军队俘虏了。我们的太空船飞行员在1级监狱中。只有一种逃生方法,它正在救助您的太空船飞行员。这意味着要从N级升级到1级。但是,由于这样做非常危险,因此您需要以最少的步骤进入监狱。

条件

  • 有四种移动方式:

    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
这个挑战让我想起了我以前使用计算器玩的游戏:我将键入一个填满屏幕的数字,并尝试尽可能地除以2、3或5,然后加/减1并继续。
Arcturus

Answers:


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 21 mod 20 mod 31 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,2015年

1
@TessellatingHeckler也许我应该更精确一些。有至少一个最佳的解决方案,即没有连续两次(向下移动)或两个(向上)运动。例如29-> 30-> 10-> 9-> 3-> 1
Jakube

我并不是说这是错误的,只是我不能“轻松地思考它为什么起作用”。我当时在想:通往1号房的最快路线是从3的幂开始,每步除以3。因此,接近三的幂的数字的最快途径是反复相减或加到三的幂,然后反复除以3。如果相反,它们以移动n / 2开始,则它们从三的幂开始变远,因此越过了最快的路线。我看不出他们将如何/总是/寻找同样短的另一条路线;看来他们现在处在一个“糟糕”选择的地区。
TessellatingHeckler,2015年

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的距离。

  • 设置列表[1] = 0表示“可在0步内到达”。
  • 对于列表中每个数字(可通过0步访问1)(即)
    • 设置1号,1号,1号* 2、2号* 3可达
  • 一步即可到达列表中的每个数字(即0,2,2,3
    • 设置编号+1,编号-1,编号* 2,编号* 3可分两步访问
  • ...等等,直到达到每个列表索引。

运行时间超过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将在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个字节

十六进制:

31c9487435405031d231dbb103f7f14a7c070f94c301d008d353e8e3ffffff5b00c3585331d2b102f7f152e8d2ffffff5a00d05a38d076019240c3

机器语言本身没有标准输入的概念。由于挑战是纯粹的计算,因此我选择编写一个函数,将输入参数输入EAX并在中返回结果AL

@Jakube很好地解释了代码背后的数学原理:搜索仅在传送点间穿插了不超过一个单级移动的路径之间进行。在输入范围的低端,性能大约为每秒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           
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.