Задача о перемещении между островами

Отдыхая в деревне, Пётр мысленно вернулся в детство и вспомнил одну из своих любимых компьютерных игр. В мире этой игры произошёл катаклизм, разорвавший его на множество островов, летающих в космическом пространстве. Со временем острова расположились в пространстве ровным рядом, один за другим.

После катаклизма, осталось всего два способа путешествовать по миру:

  1. магические паромы, перемещающиеся между соседними островами (естественно, паром может двигаться в обе стороны); таким образом с острова, который стоит на i-ом месте в космическом ряду, можно попасть на i-1 и i+1 острова;
  2. порталы, через которые можно телепортироваться между островами независимо от расстояния, но только если до катаклизма эти острова составляли один материк (любопытно, что материки в этом мире имели не названия, а номера).

Петру стало интересно, за какое минимальное количество перемещений можно добраться от первого острова до последнего. Помогите ему это выяснить (сам он всё ещё отдыхает).

Входные данные (поступают в стандартный поток ввода)
На вход вашей программе подаётся одна строка, содержащая массив целых чисел islands, где islands[i] - это числовое обозначение материка, к которому когда-то относился i-ый остров. Элементы в массиве расположены в том же порядке, что и острова в космическом пространстве.

Причём, -100 000 000 ≤ islands[i] ≤ 100 000 000, а количество островов n удовлетворяет условию 1 ≤ n ≤ 50 000.

Все входные данные наших тестов всегда соблюдают указанные параметры, дополнительные проверки не требуются.

Выходные данные (ожидаются в стандартном потоке вывода)
Одно целое число, минимальное число шагов от первого острова до последнего.

Пример 1
Ввод:

3 5 2 2 5

Вывод:

2

Простой пример для ознакомления с входными и выходными данными.

На вход подаются 5 островов.

С первого острова можно попасть только на второй с помощью парома. Порталы использовать не получится, т.к. нет других островов носящих тот же номер материка.

Со второго можно добраться на пароме обратно на первый или вперёд на третий. А также можно переместиться через портал на пятый (ведь они имеют один и тот же номер).

Таким образом мы можем переместиться от первой ячейки до последней за два шага.

Пример 2
Ввод:

11 -86 -86 201 11 86 86 86 3 201

Вывод:

3

Во втором примере потребуется уже 3 шага: Перемещение через портал до 5го острова, шаг назад до 4го (с помощью парома) и, наконец, скачок через портал до последнего.

Пример 3
Ввод:

3 2 5 2 5 2 5 3

Вывод:

1

Тривиальный пример: требуется всего одно перемещение через портал от первого острова до последнего.

Я написал следующий код:

def teleportation(islands):
    n = len(islands)
    min_teleports = 0
    i = 0
    while i < (n - 1):
        for j in reversed(range(i + 1, n)):
            island_1 = islands[i]
            island_2 = islands[j]
            if island_1 == island_2:
                i = j
                min_teleports += 1
                break
        if i < (n - 1):
            i += 1
            min_teleports += 1

    return min_teleports


if __name__ == '__main__':
    islands = [int(x) for x in input().split()]
    print(teleportation(islands))

но для второго примера он не годится. Как доработать?


Ответы (1 шт):

Автор решения: Stanislav Volodarskiy

Ваше решение предполагает что мы всегда двигаемся слева направо. Второй пример показывает что это не так, иногда выгодно двигаться влево. Не думаю что ваше решение можно доработать. Задача предполагает навигацию по графу и решается поиском в ширину. Возможно, есть другое решение, которое я не увидел.

Построим двудольный граф: в первой доле реальные острова, во второй доле дополнительные узлы в цепочке и материки. Любой путь между реальными островами в таком графе будет иметь чётную длину. Деля её пополам получим ответ к задаче.

Материк оформляется как звезда: центральный узел материка связан со всеми своими островами. В цепочку между островами вставлены дополнительные узлы.

Граф индексируется целыми числами. Реальный остров i получает индекс 2i в графе, индексы между реальными островами занимают дополнительные узлы. Индексы материков последовательные, начиная с 2n - 1, где n количество реальных островов.

Задача решается поиском в ширину по графу от узла 0 до узла 2n - 2. Множества посещённых узлов нет, его роль играет список distance:

import collections

islands = input().split()

n = len(islands)

graph = [[] for _ in range(2 * n - 1)]


def connect(j1, j2):
    graph[j1].append(j2)
    graph[j2].append(j1)


for j in range(2 * n - 2):
    connect(j, j + 1)

continents = {}
for i, c in enumerate(islands):
    k = continents.setdefault(c, len(graph))
    if k == len(graph):
        graph.append([])
    connect(2 * i, k)

distance = [0] * len(graph)
queue = collections.deque([0])
while queue:
    j = queue.popleft()
    if j == 2 * n - 2:
        break
    for k in graph[j]:
        if distance[k] == 0:
            distance[k] = distance[j] + 1
            queue.append(k)

print(distance[2 * n - 2] // 2)
$ echo 3 5 2 2 5 | python journey.py
2

$ echo 11 -86 -86 201 11 86 86 86 3 201 | python journey.py
3

$ echo 3 2 5 2 5 2 5 3 | python journey.py
1

$ time python -c "print(*[1] * 50000)" | python journey.py
1

real  0m0.205s
user  0m0.220s
sys   0m0.008s

$ time python -c "print(*range(50000))" | python journey.py
49999

real  0m0.267s
user  0m0.264s
sys   0m0.028s
→ Ссылка