达到约束的最小跳数

时间:2018-10-21 19:42:02

标签: arrays algorithm language-agnostic

我需要一些帮助,以找到达到约束条件的数组末尾所需的最小跳转数。

我有20个数字组成的数组,在0-100之间随机选择。没有重复,数字按递增顺序排列。第一个数字为0,最终数字始终为100。

int data[] = {0,8,10,18,20,25,31,33,43,45,49,51,57,62,63,69,76,84,91,100}

约束1:最大跳跃不能超过10。

约束2:跳10后必须小于10,否则没有终点。

在下面的示例中,不允许0-> 10后跟10-> 20,因为它们都相距10步。

data2[] = {0,10,20,21,25,28,31,33,43,45,49,51,57,62,63,69,76,84,91,100}

我看过Minimum number of jumps to reach end | Set 2 (O(n) solution)

但是我在应用约束时遇到了麻烦。我们将不胜感激有关如何纳入约束的任何指示。

1 个答案:

答案 0 :(得分:0)

这是Python解决方案,因为这是您最初为问题添加标签的语言之一。

这个想法是递归地尝试所有可能的路径。这在此处分为两个部分:paths生成器产生所有可能的路径,然后我们保留最短的路径。

def paths(data, curr_path=None, next_can_be_10=True):
    if curr_path is None:
        curr_path = [0]

    reachable = [v for v in data if curr_path[-1] < v <= curr_path[-1] + 9 + next_can_be_10]  # True is 1, False is 0
    if 100 in reachable:
        yield curr_path + [100]

    for candidate in reachable:
        jump = candidate - curr_path[-1]
        yield from paths(data, curr_path + [candidate], jump < 10 )


data = [0,8,10,18,20,25,31,33,43,45,49,51,57,62,63,69,76,84,91,100]            

min_len = len(data)
for p in paths(data):
    if len(p) < min_len:
        shortest = p
        min_len = len(shortest)
print(count, shortest, 'length:', min_len)

# [0, 8, 18, 25, 33, 43, 49, 57, 62, 69, 76, 84, 91, 100] length: 14 

这不是针对您任务的最有效解决方案,因为您只需要返回最短路径即可在探索所有可能性时随时选择。当路径的长度大于当前最短路径的长度时,您也可以使探索短路,尝试首先跳到最大可到达数字,依此类推。但是您可以从此处提取主要思想并编写自己的解决方案。


修改

如果原始列表变长,递归会非常慢。

这是一个直接的解决方案:我们迭代所有数据值。对于每个值,我们都跟踪导致它的最短路径。实际上,我们必须跟踪一两个不同的路径:一个导致结尾值短(小于10)的值的跳转,和/或导致结尾值跳10的值的跳转。 / p>

def shortest_path(data):
    # shortest_paths is a dict, with items of data as keys and whose values are 
    # dicts that have one or two of the keys:
    # - path_for_all_jumps, the shortest path to value without a 10-jump as last
    #   jump, which can be reused for any further jump up to 10
    # - path_for_small_jumps, a path shorter than path_for_all_jumps, but that was 
    #   reached with a final 10-jump. So, it can't be used for a 10-jump.
    shortest_paths = {data[0]: {'path_for_all_jumps': [0]}}
    for value in data[1:]:
        # In order to find the shortest paths up to value, we consider first
        # the paths already found ending 9 or less below value.
        # For each end value, we can take the shortest of path_for_all_jumps, path_for_small_jumps
        previous_paths = [min(paths.values(), key=len) for prev_val, paths in shortest_paths.items() 
                                                       if prev_val >= value-9]
        shortest_paths[value] = {}
        if previous_paths:
            shortest = min(previous_paths, key=len)
            # This path can be reused by any jump, as we jumped by less than 10
            shortest_paths[value]['path_for_all_jumps'] = shortest + [value]
        else:
            # No small jump possible to value
            shortest = None

        # We can do a 10-jump to value if value-10 has a path for all jumps: 
        try:
            shortest_after_10_jump = shortest_paths[value-10]['path_for_all_jumps']
            if shortest is None or len(shortest_after_10_jump) < len(shortest):
                # We have a better path to value, but we'll only be able to use it for small (<10)
                # jumps, as we just jumped 10
                shortest_paths[value]['path_for_small_jumps'] = shortest_after_10_jump + [value]
        except KeyError:
            # We couldn't make a 10-jump
            pass

    shortest = min(shortest_paths[100].values(), key=len)          
    return len(shortest), shortest

一些测试和计时:

data = [0,8,10,18,20,25,31,33,43,45,49,51,57,62,63,69,76,84,91,100]
print(shortest_path(data))
# 14, [0, 10, 18, 25, 33, 43, 49, 57, 62, 69, 76, 84, 91, 100]

data2 = [0, 6, 13, 20, 25, 30, 33, 36, 42, 45, 48, 53, 60, 63, 66, 69, 72, 76, 80, 83, 86, 89, 92, 95, 100]
print(shortest_path(data2))
# 14, [0, 6, 13, 20, 30, 36, 45, 53, 60, 69, 76, 83, 92, 100]

%timeit shortest_path(data2)
# 123 µs ± 939 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)