Кол-во путей
В некоторых задачах от нас требуется найти не кратчайший путь, а количество путей, удовлетворяющих заданному критерию. Рассмотри пример
Между населёнными пунктами A, B, C, D, E, F, Z построены дороги с односторонним движением. В таблице указана протяжённость каждой дороги. Отсутствие числа в таблице означает, что прямой дороги между пунктами нет. Например, из A в B есть дорога длиной 4 км, а из B в A дороги нет.
Сколько существует таких маршрутов из A в Z, которые проходят через 6 и более населен-ных пунктов? Пункты A и Z при подсчете учитывать. Два раза проходить через один пункт нельзя.
Решается задача так: в main
мы запускаем рекуррентную процедуру, которая принимает в качестве аргументов
стартовую вершину, конечную и массив положений вершин в пути. Идея заключается в том, что
элементы этого массива изначально все равны -1, кроме элемента, соответствующего начальной вершине.
В этой ячейке лежит ноль, потому что начальная точка первая в пути и имеет порядковый индекс 0. Индексация
начинается с нуля для удобства. Это поможет нам потом в заполнении массива перестановки. Дополнительно к заданию
мы найдём длины удовлетворяющих условию путей.
- Java
- C++
- Python
import java.util.Arrays;
public class Example7 {
// названия вершин
static char[] names = {'A', 'B', 'C', 'D', 'E', 'F', 'Z'};
// кол-во вершин графа
static int SIZE = 7;
// A B C D E F Z
static int[][] m = new int[][]{
{0, 4, 6, 0, 0, 0, 30}, // A
{0, 0, 3, 0, 0, 0, 0}, // B
{0, 0, 0, 11, 0, 0, 27}, // C
{0, 0, 0, 0, 4, 7, 10}, // D
{0, 0, 0, 0, 0, 4, 8}, // E
{0, 0, 0, 0, 5, 0, 2}, // F
{29, 0, 0, 0, 0, 0, 0}, // Z
};
// кол-во путей
static int pathCnt = 0;
// сгенерировать пути
static void generatePaths(int currentPoint, int target, int[] inPathPositions, int stepCnt) {
// если мы дошли до целевой вершины
if (currentPoint == target) {
// шагов всегда меньше на 1, чем вершин в пути, поэтому сравниваем
// пятью вместо шести
if (stepCnt >= 5) {
// инициализируем массив порядковых номеров вершин
int[] pointOrder = new int[SIZE];
Arrays.fill(pointOrder,-1);
// перебираем вершины
for (int i = 0; i < SIZE; i++) {
// если порядковый номер вершины в пути задан
if (inPathPositions[i] != -1)
// порядковый номер вершины
pointOrder[inPathPositions[i]] = i;
}
// находим, сколько вершин мы использовали при построении пути
int realSize = SIZE;
for (int i = SIZE - 1; i > 0; i--) {
if (pointOrder[i] != -1) {
realSize = i+1;
break;
}
}
// находим длину пути
int pathLength = 0;
for (int i = 0; i < realSize-1; i++) {
pathLength += m[pointOrder[i]][pointOrder[i + 1]];
}
System.out.print(pathLength + ": ");
// выводим вершины, которые участвую в пути в порядке следования
for (int i = 0; i < realSize; i++) {
System.out.print(names[pointOrder[i]]);
}
System.out.println();
// увеличиваем кол-во путей на 1
pathCnt++;
}
} else { // иначе
// перебираем все вершины
for (int i = 0; i < SIZE; i++) {
// если порядковый номер вершины в пути ещё не задан
// и между обрабатываемой вершиной и перебираемой есть ребро
if (inPathPositions[i] == -1 && m[currentPoint][i] > 0) {
// получаем копию порядковых номеров массива
int[] copyInPathPositions = Arrays.copyOf(inPathPositions, SIZE);
// задаём порядковый номер для перебираемой вершины
copyInPathPositions[i] = stepCnt + 1;
// генерируем путь через эту вершину
generatePaths(i, target, copyInPathPositions, stepCnt + 1);
}
}
}
}
// главный метод программы
public static void main(String[] args) {
// положение вершин в пути
int[] inPathPositions = new int[SIZE];
// все вершины изначально не участвуют в пути
Arrays.fill(inPathPositions,-1);
// начальная точка A
int start = 0;
// конечная точка Z
int target = 6;
// текущая точка имеет нулевой порядковый индекс
inPathPositions[start] = 0;
// ищем все пути между двумя точками
generatePaths(start, target, inPathPositions, 0);
// выводим ответ
System.out.println("path cnt: " + pathCnt);
}
}
#include <string>
#include <iostream>
#include <bits/stdc++.h>
// кол-во вершин графа
const int SIZE = 7;// названия вершин
const std::string names[SIZE] = {"A", "B", "C", "D", "E", "F", "Z"};
// A B C D E F Z
int m[SIZE][SIZE] = {
{0, 4, 6, 0, 0, 0, 30}, // A
{0, 0, 3, 0, 0, 0, 0}, // B
{0, 0, 0, 11, 0, 0, 27}, // C
{0, 0, 0, 0, 4, 7, 10}, // D
{0, 0, 0, 0, 0, 4, 8}, // E
{0, 0, 0, 0, 5, 0, 2}, // F
{29, 0, 0, 0, 0, 0, 0}, // Z
};
// кол-во путей
int pathCnt = 0;
// сгенерировать пути
static void generatePaths(int currentPoint, int target, const int inPathPositions[], int stepCnt) {
// если мы дошли до целевой вершины
if (currentPoint == target) {
// шагов всегда меньше на 1, чем вершин в пути, поэтому сравниваем
// пятью вместо шести
if (stepCnt >= 5) {
// инициализируем массив порядковых номеров вершин
int pointOrder[SIZE];
std::fill(std::begin(pointOrder), std::begin(pointOrder) + SIZE, -1);
// перебираем вершины
for (int i = 0; i < SIZE; i++) {
// если порядковый номер вершины в пути задан
if (inPathPositions[i] != -1)
// порядковый номер вершины
pointOrder[inPathPositions[i]] = i;
}
// находим, сколько вершин мы использовали при построении пути
int realSize = SIZE;
for (int i = SIZE - 1; i > 0; i--) {
if (pointOrder[i] != -1) {
realSize = i + 1;
break;
}
}
// находим длину пути
int pathLength = 0;
for (int i = 0; i < realSize - 1; i++) {
pathLength += m[pointOrder[i]][pointOrder[i + 1]];
}
std::cout << pathLength << " ";
// выводим вершины, которые участвую в пути в порядке следования
for (int i = 0; i < realSize; i++) {
std::cout << names[pointOrder[i]];
}
std::cout << std::endl;
// увеличиваем кол-во путей на 1
pathCnt++;
}
} else { // иначе
// перебираем все вершины
for (int i = 0; i < SIZE; i++) {
// если порядковый номер вершины в пути ещё не задан
// и между обрабатываемой вершиной и перебираемой есть ребро
if (inPathPositions[i] == -1 && m[currentPoint][i] > 0) {
int copyInPathPositions[SIZE];
for (int j = 0; j < SIZE; j++) {
copyInPathPositions[j] = inPathPositions[j];
}
// задаём порядковый номер для перебираемой вершины
copyInPathPositions[i] = stepCnt + 1;
// генерируем путь через эту вершину
generatePaths(i, target, copyInPathPositions, stepCnt + 1);
}
}
}
}
// главный метод программы
int main() {
// положение вершин в пути
int inPathPositions[SIZE];
std::fill(std::begin(inPathPositions), std::begin(inPathPositions) + SIZE, -1);
// начальная точка A
int start = 0;
// конечная точка Z
int target = 6;
// текущая точка имеет нулевой порядковый индекс
inPathPositions[start] = 0;
// ищем все пути между двумя точками
generatePaths(start, target, inPathPositions, 0);
// выводим ответ
std::cout << "path cnt: " << pathCnt;
}
# кол-во вершин (используется для удобства)
SIZE = 7
# названия вершин
names = ['A', 'B', 'C', 'D', 'E', 'F', 'Z']
# П1 П2 П3 П4 П5 П6 П7
m = [
[0, 4, 6, 0, 0, 0, 30], # A
[0, 0, 3, 0, 0, 0, 0], # B
[0, 0, 0, 11, 0, 0, 27], # C
[0, 0, 0, 0, 4, 7, 10], # D
[0, 0, 0, 0, 0, 4, 8], # E
[0, 0, 0, 0, 5, 0, 2], # F
[29, 0, 0, 0, 0, 0, 0] # Z
]
pathCnt = 0
# сгенерировать пути
def generate_paths(currentPoint, target, inPathPositions, stepCnt):
global pathCnt
# если мы дошли до целевой вершины
if currentPoint == target:
# шагов всегда меньше на 1, чем вершин в пути, поэтому сравниваем
# пятью вместо шести
if stepCnt >= 5:
# инициализируем массив порядковых номеров вершин
pointOrder = [-1] * SIZE
# перебираем вершины
for i in range(SIZE):
# если порядковый номер вершины в пути задан
if inPathPositions[i] != -1:
# порядковый номер вершины
pointOrder[inPathPositions[i]] = i
# находим, сколько вершин мы использовали при построении пути
realSize = SIZE
for i in range(SIZE):
if pointOrder[i] == -1:
realSize = i
break
# находим длину пути
pathLength = 0
for i in range(realSize - 1):
pathLength += m[pointOrder[i]][pointOrder[i + 1]]
# выводим вершины, которые участвую в пути в порядке следования
out = str(pathLength) + ": "
for i in range(realSize):
out += names[pointOrder[i]]
print(out)
# увеличиваем кол-во путей на 1
pathCnt = pathCnt + 1
else: # иначе
# перебираем все вершины
for i in range(SIZE):
# если порядковый номер вершины в пути ещё не задан
# и между обрабатываемой вершиной и перебираемой есть ребро
if inPathPositions[i] == -1 and m[currentPoint][i] > 0:
# получаем копию порядковых номеров массива
copyInPathPositions = inPathPositions.copy()
# задаём порядковый номер для перебираемой вершины
copyInPathPositions[i] = stepCnt + 1
# генерируем путь через эту вершину
generate_paths(i, target, copyInPathPositions, stepCnt + 1)
# главный метод программы
if __name__ == '__main__':
# все вершины изначально не участвуют в пути
inPathPositions = [-1] * SIZE
# начальная точка A
start = 0
# конечная точка Z
target = 6
# текущая точка имеет нулевой порядковый индекс
inPathPositions[start] = 0
# ищем все пути между двумя точками
generate_paths(start, target, inPathPositions, 0)
# выводим ответ
print("path cnt: ", pathCnt)
На выходе получим:
28: ABCDEFZ
30: ABCDEZ
38: ABCDFEZ
27: ABCDFZ
27: ACDEFZ
37: ACDFEZ
path cnt: 6
Т.е. ответ на вопрос задачи: 6 путей