Un approccio di programmazione dinamica dovrebbe funzionare.
Per ogni cella:
-
Riduci il problema di trovare il percorso discendente migliore (più lungo, più ripido) da lì al problema di trovare il percorso migliore da ciascuno dei suoi vicini che è più piccolo e quindi aggiungere il passaggio a quel vicino per ottenere il miglior percorso dalla cella in questione.
-
Memo (r) ize il percorso migliore da ogni cella lungo il percorso.
-
In realtà non salva un percorso esplicito, salva solo la cella successiva sul percorso (in questo modo il percorso è lì, solo implicitamente).
Dovrebbe essere eseguito in tempo lineare rispetto alla dimensione della matrice se lo fai correttamente ... a meno che non mi fraintenda il problema, naturalmente.
Il codice potrebbe essere simile a:
public class BestDynamicDescendingPathFinder {
public static int[][] example = new int[][]{{4, 8, 7, 3},{2, 5, 9, 3},{6, 3, 2, 5},{4, 4, 1, 6}};
public static void main(String[] args)
{
BestDynamicDescendingPathFinder finder = new BestDynamicDescendingPathFinder(example);
System.out.println("Best overall: " + Arrays.toString(finder.find()));
System.out.println("Best starting from some other cell: " + Arrays.toString(finder.unfoldBestPathFromCell(3, 3)));
}
private int[][] matrix;
private PathInformation[][] informationForBestPathFromCellMemory;
public BestDynamicDescendingPathFinder(int[][] aMatrix)
{
informationForBestPathFromCellMemory = new PathInformation[aMatrix.length][];
matrix = new int[aMatrix.length][];
for(int i = 0; i < aMatrix.length; i++)
{
informationForBestPathFromCellMemory[i] = new PathInformation[aMatrix[i].length];
matrix[i] = new int[aMatrix[i].length];
for(int j = 0; j < aMatrix[i].length; j++)
{
matrix[i][j] = aMatrix[i][j];
}
}
}
// find the best path by getting the best starting cell and unfolding the information for it
public int[] find()
{
int currentBestStartingCellColumn = 0;
int currentBestStartingCellRow = 0;
for(int i = 0; i < matrix.length; i++)
{
for(int j = 0; j < matrix[i].length; j++)
{
if(getInformationForBestPathFromCell(i, j).compareTo(getInformationForBestPathFromCell(currentBestStartingCellColumn, currentBestStartingCellRow)) == 1){
currentBestStartingCellColumn = i;
currentBestStartingCellRow = j;
}
}
}
return unfoldBestPathFromCell(currentBestStartingCellColumn, currentBestStartingCellRow);
}
// unfold the best path (starting) from a cell by walking the PathInformation structures in memory
private int[] unfoldBestPathFromCell(int colNum, int rowNum)
{
PathInformation currentCellInformation = getInformationForBestPathFromCell(colNum, rowNum);
int[] path = new int[currentCellInformation.length];
path[0] = matrix[colNum][rowNum];
int idx = 1;
while(currentCellInformation.length > 1)
{
path[idx] = matrix[currentCellInformation.nextCellColumn][currentCellInformation.nextCellRow];
idx++;
currentCellInformation = getInformationForBestPathFromCell(currentCellInformation.nextCellColumn, currentCellInformation.nextCellRow);
}
return path;
}
// get the information for the best path (starting) from a cell: from memory if available or calculate otherwise
private PathInformation getInformationForBestPathFromCell(int colNum, int rowNum)
{
if(informationForBestPathFromCellMemory[colNum][rowNum] == null)
{
informationForBestPathFromCellMemory[colNum][rowNum] = calculateInformationForBestPathFromCell(colNum, rowNum);
}
return informationForBestPathFromCellMemory[colNum][rowNum];
}
// calculate the information for the best path (starting) from a cell by using the information for best paths from neighboring cells
private PathInformation calculateInformationForBestPathFromCell(int colNum, int rowNum)
{
List<PathInformation> possiblePathsFromCell = new ArrayList<PathInformation>();
if(colNum != 0 && matrix[colNum - 1][rowNum] < matrix[colNum][rowNum])
{
PathInformation p = getInformationForBestPathFromCell(colNum - 1, rowNum);
possiblePathsFromCell.add(new PathInformation(p.length + 1, matrix[colNum][rowNum], p.endValue, colNum - 1, rowNum));
}
if(colNum != matrix.length - 1 && matrix[colNum + 1][rowNum] < matrix[colNum][rowNum])
{
PathInformation p = getInformationForBestPathFromCell(colNum + 1, rowNum);
possiblePathsFromCell.add(new PathInformation(p.length + 1, matrix[colNum][rowNum], p.endValue, colNum + 1, rowNum));
}
if(rowNum != 0 && matrix[colNum][rowNum - 1] < matrix[colNum][rowNum])
{
PathInformation p = getInformationForBestPathFromCell(colNum, rowNum - 1);
possiblePathsFromCell.add(new PathInformation(p.length + 1, matrix[colNum][rowNum], p.endValue, colNum, rowNum - 1));
}
if(rowNum != matrix[colNum].length -1 && matrix[colNum][rowNum + 1] < matrix[colNum][rowNum])
{
PathInformation p = getInformationForBestPathFromCell(colNum, rowNum + 1);
possiblePathsFromCell.add(new PathInformation(p.length + 1, matrix[colNum][rowNum], p.endValue, colNum, rowNum + 1));
}
if(possiblePathsFromCell.isEmpty())
{
return new PathInformation(1, matrix[colNum][rowNum], matrix[colNum][rowNum], -1, -1);
}
return Collections.max(possiblePathsFromCell);
}
}
public class PathInformation implements Comparable<PathInformation>
{
int length;
int startValue;
int endValue;
int nextCellColumn;
int nextCellRow;
public PathInformation(int length, int startValue, int endValue, int nextCellColumn, int nextCellRow)
{
this.length = length;
this.startValue = startValue;
this.endValue = endValue;
this.nextCellColumn = nextCellColumn;
this.nextCellRow = nextCellRow;
}
@Override
public int compareTo(PathInformation other) {
if(this.length < other.length || (this.length == other.length && this.startValue - this.endValue < other.startValue - other.endValue)){
return -1;
}
if(this.length > other.length || (this.length == other.length && this.startValue - this.endValue > other.startValue - other.endValue)){
return 1;
}
return 0;
}
}