我接受了采访,无法想到针对此问题的清晰/最佳解决方案。
给出2个数字A和B,我们需要通过最少的以下操作次数将数字A转换为B:
例如:如果a = 3和b = 7,则程序应输出2。
第一次操作:* 2-> 3 * 2 = 6。
第二次操作:+1-> 6 + 1 = 7。
例如:如果a = 10且b = 60,则程序应输出2。
第一次操作:* 2-> 10 * 2 = 20。
第二次操作:* 3-> 20 * 3 = 60
由于我们可以在2次操作后将m(10)更改为n(60),答案是2。
试图使用动态编程和递归方法,但无济于事。有提示吗?
答案 0 :(得分:3)
如其他答案中所述,可以使用BFS在图中其节点与数字相对应,其边沿与运算相对应的图中来实现。
有趣的是,有时,最佳路径需要包含相当大的数字(大于3 * max(A,B))。 以下是其中包含如此大量数字的最佳路径的示例:
a = 82, b = 73
optimal path:
[82, 164, 328, 656, 657, 219, 73] (6 operations)
optimal path if paths with values larger than 3 * max(a, b) are discarded:
[82, 81, 162, 54, 108, 216, 72, 73] (7 operations)
以下是此BFS解决方案的python实现:
def solve(a, b, max_n=None):
# the bfs queue
queue = []
# length[i] = length of the shortest
# path to get from `a' to `i'
length = {}
# previous[i] = previous value reached
# in the shortest path from `a' to `i'
previous = {}
# node with value `a' is the first in the path
queue.append(a)
length[a] = 0
previous[a] = None
while True:
val = queue.pop(0)
# add an element to the queue (if it was not
# already visited, and eventually not above
# some limit)
def try_add(next_val):
if max_n is not None and next_val > max_n:
return
if next_val in length:
return
queue.append(next_val)
length[next_val] = length[val] + 1
previous[next_val] = val
try_add(val + 1)
try_add(val - 1)
try_add(val * 2)
if val % 2 == 0:
try_add(val // 2)
try_add(val * 3)
if val % 3 == 0:
try_add(val // 3)
# check whether we already have a solution
if b in length:
break
path = [b]
while True:
if path[-1] == a:
break
else:
path.append(previous[path[-1]])
path.reverse()
return path
if __name__ == '__main__':
a = 82
b = 73
path = solve(a, b)
print(len(path), ': ', path)
path = solve(a, b, 3 * max(a, b))
print(len(path), ': ', path)
答案 1 :(得分:1)
处理数字作为图形的节点,操作作为边。使用BFS查找从A到B的最短路径。
我认为您可以将节点的上限设置为A和B的绝对值的3倍,以最大程度地减少步数,但这不是必需的。
空间和时间复杂度与答案成正比,例如如果答案是2,则在最坏的情况下,我们必须访问6 * 2 = 12个节点。
答案 2 :(得分:1)
这是BFS Javascript解决方案:
const findPath = (ops) => (A, B) => {
const queue = new Set() .add ( [A, []] )
const paths = new Map()
while (queue .size !== 0 && !paths .has (B)) {
const next = [...queue] [0]
const [n, p] = next
ops.forEach((fn) => {
const m = fn(n);
if (Number.isInteger(m)) {
if (!paths.has(m)) {
queue.add([m, [...p, n]])
paths.set(m, [...p, n])
}
queue.delete(next)
}
})
}
return paths.get(B)
}
const ops = [n => n + 1, n => n - 1, n => 2 * n, n => 3 * n, n => n / 2, n => n / 3]
console .log (
findPath (ops) (82, 73)
)
我们仍然要处理数字队列,并记录每个找到的数字的路径的字典,并继续对其进行测试,直到队列为空(这些操作不会发生,但是其他操作可能会让我们耗尽它)或我们找到了目标。对于每个数字,我们运行每个操作,对于整数结果,如果尚未找到,请将其添加到我们的结构中。
这里没有什么可以阻止链条失去控制的。目前尚不清楚我们将如何做。而且显然可以通过不同的操作来实现:如果我们有add 2
,subtract 2
和double
,我们将永远无法从2
过渡到3
。这种算法永远不会停止。
虽然可以将其当然地转换为递归算法,但朴素的递归不太可能成功,因为它将首先进行深度优先并且通常会丢失该值并且永不停止。