Flat Preloader Icon

Project Euler (answers)

Проект Эйлера: Ответ и решение задачи 18 (Максимальная сумма пути 1)

Проект Эйлера (18 задача) познакомит Вас с динамическим программированием (разбиение решения сложной задачи на более простые подзадачи)

Узнать больше в телеграм-канале: ProjectEuler++

Условия задачи

Начиная в вершине треугольника (см. пример ниже) и перемещаясь вниз на смежные числа, максимальная сумма до основания составляет 23.

Задание 1 - проект Эйлера (18 задача)

То есть, 3 + 7 + 4 + 9 = 23.

Найдите максимальную сумму пути от вершины до основания следующего треугольника:

Задание 2 - проект Эйлера (18 задача)

Примечание: Так как в данном треугольнике всего 16384 возможных маршрута от вершины до основания, эту задачу можно решить проверяя каждый из маршрутов. Однако похожая Задача 67 с треугольником, состоящим из сотни строк, не решается перебором (brute force) и требует более умного подхода! ;o)

Всегда Вам рады)

Решение (проект Эйлера - 18 задача)

Проект Эйлера (18 задача) - решение
Проект Эйлера (18 задача) - решение

Проблемы наивных алгоритмов

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

  • 75 -> 95 -> 47

Однако остановимся на этом и подумаем, нет ли лучшего пути.

  • 75 + 95 + 47 = 217

Что будет, если от 65 пойти не к большему (95), а к меньшему (64) числу:

  • 75 -> 64 -> 82
  • 75 + 64 + 82 = 221 > 217

Уже на начальном участке видно, что такой подход (на каждой развилке идти на большее число) в итоге не гарантирует максимальную длину пути.

Неужели придется перебирать все варианты пути (16384 маршрута) и сравнивать их суммы между собой? Конечно, такой подход вернет правильный результат, однако, считаться будет долго.

 

Описание динамического алгоритма

Решением будет разбитие задачи на две более легкие:

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

Возьмем для примера крайнее левое число из предпоследнего ряда – 64. От него можно пойти либо на 4, либо на 62. Таким образом максимальная сумма пути для этой развилки:

  • 64 + 62 = 126

Пройдем дальше по ряду и запишем получившиеся данные в следующую ячейку.

  • 64 + 98 = 164

Пройдем весь ряд до конца:

  • 63, 66, 04, 68… предпоследний ряд
  • 04, 62, 98, 27… последний ряд 

…и заменим значения в нем таким же образом:

  • 126, 164, 102… предпоследний ряд
  • 04, 62, 98, 27… последний ряд: 

После этого переходим на ряд выше и заполняем его:

  • 91, 71, 52, 38… ряд выше:
  • 126, 164, 102… предпоследний ряд
  • 04, 62, 98, 27… последний ряд 

Число 91 теперь также будет выбирать путь к максимальному числу. Однако выбирать оно будет не между числами (63, 66), а между ранее посчитанными максимальными путями для развилок (126, 164).

В итоге необходимо просто пройти пирамиду сверху вниз и на каждой развилке посчитать сумму с максимальным числом из нижнего ряда. За один проход ответ будет найден и записан вместо вершины (75).

Пояснения к коду программы

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

Это сделано, потому что ряды чисел пирамиды – разной длины. Однако, за счет того, что при инициализации массива ячейки, не занятые числами будут заполнены нулями, это позволит останавливать  прохождение ряда чисел  при встрече с нулем.

Конструкция: 

  • while(arr[row][indx]) 

…будет работать до тех пор, пока значение в ячейке массива arr[row][indx] не нулевое.

Тернарная операция:

  • arr[row][indx] += (arr[row + 1][indx] > arr[row + 1][indx + 1]) ? arr[row + 1][indx] : arr[row + 1][indx + 1];

… прибавляет к значению ячейки массива arr[row][indx] максимальное из двух смежных ячеек нижестоящего ряда и может быть записана иначе:

  • if(arr[row + 1][indx]> arr[row + 1][indx + 1])
    • arr[row][indx] = arr[row][indx] + arr[row + 1][indx];
  • else
    • arr[row][indx] = arr[row][indx] +arr[row + 1][indx + 1];

Измеряем время работы программы

Для измерения времени работы программы подключаем библиотеку time.h:

  • #include <time.h> 

С помощью функции clock() измеряем время начала (begin) и окончания (end) работы программы. В итоге время работы будем находить как разность между ними.

* Примечания:

clock_t это тип данных, определенный через typedef. По сути это обычный целочисленный long

(double) приводит число (в данном случае – результат вычислений end – begin) к вещественному типу данных double (числа с точкой)

CLOCKS_PER_SEC – это константа, сохраненная в библиотеке и зависящая от типа устройства, на котором запускается программа. В данном случае – это просто 1000

Ответ и скорость работы программы (проект Эйлера - 18 задача)

Проект Эйлера (18 задача) - ответ
18 задача ответ

В итоге ответ на 18 задачу проекта Эйлера составил 1074, программа считает ответ мгновенно. 

По традиции хотелось бы выразить глубочайшую признательность Сергею Балакиреву за его титанический труд по созданию бесплатных курсов и в частности, за “Язык программирования C/C++ для начинающих“.

проект Эйлера
Курс "C/C++ для начинающих"

Есть вопросы, господа? Отвечаем спокойно, четко и только по делу))

Есть вопросы, господа?

Проект Эйлера (ответы) | © 2023-2024 | eulerproject.ru

Проект Эйлера (ответы) | © 2023-2024 | eulerproject.ru

© 2023-2024 | eulerproject.ru