Problem (Skiing):
Each number represents the elevation of that area of the mountain.
From each area (i.e. box) in the grid, you can go north, south, east, west - but only if the elevation of the area you are going into is less than the one you are in (i.e. you can only ski downhill).
You can start anywhere on the map and you are looking for a starting point with the longest possible path down as measured by the number of boxes you visit.
And if there are several paths down of the same length, you want to take the one with the steepest vertical drop, i.e. the largest difference between your starting elevation and your ending elevation.
Goal:
- Find the longest path following these rules. Longest path means the maximum number of nodes where we are always going down.
- If we have many longest paths with equal distance (number of nodes), then we should get the one with the maximum drop (value of 1st node - value of the last node).
The aim is to run this code for a 1000 x 1000 matrix. As for now, running for just a 4 x 4, it's taking minutes. Any idea on how to reduce the complexity regarding time and space?
myMatrix = [[4, 5, 2],[1, 1, 6],[8, 7, 5]]
def getAllValidSkiingPathsFrom(myMatrix):
dctOfMatrix = {}
for row in range(len(myMatrix)):
for column in range(len(myMatrix[0])):
currPoint = (column, row)
dctOfMatrix[currPoint] = myMatrix[row][column]
lstIndicesOfAllMatrixPoints = list(dctOfMatrix.keys())
setAllPossiblePaths = set()
from itertools import permutations
for pathCandidate in permutations(lstIndicesOfAllMatrixPoints):
lstPossiblePath = []
prevIndexTuple = pathCandidate[0]
lstPossiblePath.append(prevIndexTuple)
for currIndexTuple in pathCandidate[1:]:
if abs(currIndexTuple[0]-prevIndexTuple[0]) + abs(currIndexTuple[1]-prevIndexTuple[1]) > 1:
break # current path indices not allowed in path (no diagonals or jumps)
else:
if dctOfMatrix[currIndexTuple] >= dctOfMatrix[prevIndexTuple]:
break # only "down" is allowed for "skiing"
else:
lstPossiblePath.append(currIndexTuple)
prevIndexTuple = currIndexTuple
if len(lstPossiblePath) > 1 and tuple(lstPossiblePath) not in setAllPossiblePaths:
setAllPossiblePaths.add(tuple(lstPossiblePath))
return setAllPossiblePaths, dctOfMatrix
setAllPossiblePaths, dctOfMatrix = getAllValidSkiingPathsFrom(myMatrix)
printedPath = []
bestPath = []
for path in setAllPossiblePaths:
for point in path:
printedPath.append(dctOfMatrix[point])
if len(printedPath) > len(bestPath): # Looking for the path with a maximum distance
bestPath = printedPath
if len(printedPath) == len(bestPath): # If we have more than one path with a maximum distance we look for the drop
if (printedPath[0]-printedPath[-1]) > (bestPath[0]-bestPath[-1]):
bestPath = printedPath
printedPath = []
print("Path -->", bestPath)
print("Distance -->", len(bestPath))
print("Drop -->", bestPath[0]-bestPath[-1])
Sample input matrix:
4 5 2
1 1 6
8 7 5
Output:
Path --> [8, 7, 1]
Distance --> 3
Drop --> 7
Explanation:
The longest path would be 8-7-1 as it has the maximum distance = 3.
There are other paths with the same maximum distance which are 8-7-5 and 5-4-1. However, 8-7-1 has the maximum drop = 7 (8-1).