151 lines
4.6 KiB
C#
151 lines
4.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
|
|
public static class SpaceEfficientMazeSolver
|
|
{
|
|
// Memory-limited DFS that only keeps O(√n) visited nodes in memory
|
|
// Recomputes paths when needed, trading time for space
|
|
public static MazeResult MemoryLimitedDFS(bool[,] maze, int memoryLimit)
|
|
{
|
|
var sw = Stopwatch.StartNew();
|
|
long memBefore = GC.GetTotalMemory(true);
|
|
|
|
int rows = maze.GetLength(0);
|
|
int cols = maze.GetLength(1);
|
|
int nodesExplored = 0;
|
|
bool pathFound = false;
|
|
int pathLength = 0;
|
|
|
|
// Limited memory for visited nodes - simulates √n space
|
|
var limitedVisited = new HashSet<(int, int)>(memoryLimit);
|
|
var currentPath = new HashSet<(int, int)>(); // Track current recursion path to prevent cycles
|
|
|
|
bool DfsWithRecomputation(int r, int c, int depth)
|
|
{
|
|
nodesExplored++;
|
|
|
|
// Goal reached
|
|
if (r == rows - 1 && c == cols - 1)
|
|
{
|
|
pathLength = depth;
|
|
return true;
|
|
}
|
|
|
|
var current = (r, c);
|
|
|
|
// Prevent cycles in current path
|
|
if (currentPath.Contains(current))
|
|
return false;
|
|
|
|
currentPath.Add(current);
|
|
|
|
// Add to limited visited set (may evict old entries)
|
|
if (limitedVisited.Count >= memoryLimit && !limitedVisited.Contains(current))
|
|
{
|
|
// Evict oldest entry (simulate FIFO for simplicity)
|
|
var toRemove = limitedVisited.First();
|
|
limitedVisited.Remove(toRemove);
|
|
}
|
|
limitedVisited.Add(current);
|
|
|
|
int[] dr = { 0, 1, 0, -1 };
|
|
int[] dc = { 1, 0, -1, 0 };
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
int nr = r + dr[i], nc = c + dc[i];
|
|
if (nr >= 0 && nr < rows && nc >= 0 && nc < cols && maze[nr, nc])
|
|
{
|
|
if (DfsWithRecomputation(nr, nc, depth + 1))
|
|
{
|
|
currentPath.Remove(current);
|
|
pathFound = true;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
currentPath.Remove(current);
|
|
return false;
|
|
}
|
|
|
|
pathFound = DfsWithRecomputation(0, 0, 1);
|
|
|
|
sw.Stop();
|
|
long memAfter = GC.GetTotalMemory(true);
|
|
|
|
return new MazeResult
|
|
{
|
|
Elapsed = sw.Elapsed,
|
|
MemoryUsage = memAfter - memBefore,
|
|
PathFound = pathFound,
|
|
PathLength = pathLength,
|
|
NodesExplored = nodesExplored
|
|
};
|
|
}
|
|
|
|
// Iterative deepening DFS - uses O(log n) space but recomputes extensively
|
|
public static MazeResult IterativeDeepeningDFS(bool[,] maze)
|
|
{
|
|
var sw = Stopwatch.StartNew();
|
|
long memBefore = GC.GetTotalMemory(true);
|
|
|
|
int rows = maze.GetLength(0);
|
|
int cols = maze.GetLength(1);
|
|
int nodesExplored = 0;
|
|
bool pathFound = false;
|
|
int pathLength = 0;
|
|
|
|
// Try increasing depth limits
|
|
for (int maxDepth = 1; maxDepth <= rows * cols; maxDepth++)
|
|
{
|
|
bool DepthLimitedDFS(int r, int c, int depth)
|
|
{
|
|
nodesExplored++;
|
|
|
|
if (depth > maxDepth) return false;
|
|
|
|
if (r == rows - 1 && c == cols - 1)
|
|
{
|
|
pathLength = depth;
|
|
return true;
|
|
}
|
|
|
|
int[] dr = { 0, 1, 0, -1 };
|
|
int[] dc = { 1, 0, -1, 0 };
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
int nr = r + dr[i], nc = c + dc[i];
|
|
if (nr >= 0 && nr < rows && nc >= 0 && nc < cols && maze[nr, nc])
|
|
{
|
|
if (DepthLimitedDFS(nr, nc, depth + 1))
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
if (DepthLimitedDFS(0, 0, 0))
|
|
{
|
|
pathFound = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
sw.Stop();
|
|
long memAfter = GC.GetTotalMemory(true);
|
|
|
|
return new MazeResult
|
|
{
|
|
Elapsed = sw.Elapsed,
|
|
MemoryUsage = memAfter - memBefore,
|
|
PathFound = pathFound,
|
|
PathLength = pathLength,
|
|
NodesExplored = nodesExplored
|
|
};
|
|
}
|
|
} |