//uwagi do wszystkich części (graf najkrótszych ścieżek) // Jeżeli nie ma ścieżki pomiędzy miastami A i B to tworzymy sztuczną krawędź od A do B o karnym koszcie 10 000. // return: tablica kosztów organizacji ŚDM dla poszczególnym miast gdzie // za koszt organizacji ŚDM uznajemy sumę kosztów dostania się ze wszystkim miast do danego miasta, bez uwzględnienia kosztów przechodzenia przez miasta. // minCost: najmniejszy koszt // paths: graf skierowany zawierający drzewo najkrótyszch scieżek od wszyskich miast do miasta organizującego ŚDM (miasta z najmniejszym kosztem organizacji). public int[] FindBestLocationWithoutCityCosts(out int minCost, out Graph paths) { //graphExport.Export(RoadsGraph); int tmpCost = 0; PathsInfo[][] pathsInfos = new PathsInfo[RoadsGraph.VerticesCount][]; //pathsInfos[v] -> PathsInfo from vertex v to every other vertex minCost = Int32.MaxValue; int[] forEachCityCosts = new int[RoadsGraph.VerticesCount]; Graph shortestPaths = RoadsGraph.IsolatedVerticesGraph(true, RoadsGraph.VerticesCount); int minVertex = -1; //calculate minimal cost for (int v = 0; v < RoadsGraph.VerticesCount; v++) { RoadsGraph.DijkstraShortestPaths(v, out pathsInfos[v]); for (int i = 0; i < pathsInfos.Length; i++) { //if there is no connection, add connection of cost noEdgePenalty tmpCost += double.IsNaN(pathsInfos[v][i].Dist) ? noEdgePenalty : (int)pathsInfos[v][i].Dist; } forEachCityCosts[v] = tmpCost; if (minCost > tmpCost) { minCost = tmpCost; minVertex = v; //minVertexPathsInfos = pathsInfos; } tmpCost = 0; } //construct paths from all cities to minimal cost city for (int v = 0; v < RoadsGraph.VerticesCount; v++) { if (v == minVertex) { continue; } Edge[] path = PathsInfo.ConstructPath(v, minVertex, pathsInfos[v]); //if there is no connection, add connection of cost noEdgePenalty if (null == path) { shortestPaths.AddEdge(v, minVertex, noEdgePenalty); } else { foreach (var e in path) { shortestPaths.AddEdge(e); } } } paths = shortestPaths; return(forEachCityCosts); }
/// <summary> /// Algorytm znajdujący drugą pod względem długości najkrótszą ścieżkę między a i b. /// Możliwe, że jej długość jest równa najkrótszej (jeśli są dwie najkrótsze ścieżki, /// algorytm zwróci jedną z nich). /// Wymagamy, aby na ścieżce nie było powtórzeń wierzchołków ani krawędzi. /// Można założyć, że a!=b oraz że w grafie nie występują pętle. /// </summary> /// <remarks> /// Wymagana złożoność to O(nD), gdzie D jest złożonością implementacji algorytmu Dijkstry w bibliotece Graph. /// </remarks> /// <param name="g"></param> /// <param name="path">null jeśli druga ścieżka nie istnieje, wpp ściezka jako ciąg krawędzi</param> /// <returns>null jeśli druga ścieżka nie istnieje, wpp długość tej ścieżki</returns> public static double?FindSecondSimpleShortestPath(this Graph g, int a, int b, out Edge[] path) { path = null; PathsInfo[] d, best = null; double min = Double.MaxValue; g.DijkstraShortestPaths(a, out d); if (d[b].Dist.IsNaN()) { return(null); } Edge[] firstpath = PathsInfo.ConstructPath(a, b, d); foreach (Edge e in firstpath) { g.DelEdge(e); g.DijkstraShortestPaths(a, out d); if (d[b].Dist < min) { min = d[b].Dist; best = d; } g.AddEdge(e); } if (min == Double.MaxValue) { return(null); } path = PathsInfo.ConstructPath(a, b, best); return(min); }
/// <summary> /// Sprawdza możliwość przejazdu między dzielnicami-wielokątami district1 i district2, /// tzn. istnieją para ulic, pomiędzy którymi jest przejazd /// oraz fragment jednej ulicy należy do obszaru jednej z dzielnic i fragment drugiej należy do obszaru drugiej dzielnicy /// </summary> /// <returns>Informacja czy istnieje przejazd między dzielnicami</returns> public bool CheckDistricts(Street[] streets, Point[] district1, Point[] district2, out List <int> path, out List <Point> intersections) { AdjacencyListsGraph <SimpleAdjacencyList> g = new AdjacencyListsGraph <SimpleAdjacencyList>(false, streets.Length + 2); path = new List <int>(); intersections = new List <Point>(); int s = streets.Length; int d1 = streets.Length + 1; int d2 = streets.Length; //Construct graph for (int i = 0; i < s; i++) { for (int j = 0; j < district1.Length; j++) { Street temp = new Street(district1[j], district1[(j + 1) % district1.Length]); if (CheckIntersection(temp, streets[i]) == 1) { g.AddEdge(i, d1); } } for (int j = 0; j < district2.Length; j++) { Street temp = new Street(district2[j], district2[(j + 1) % district2.Length]); if (CheckIntersection(temp, streets[i]) == 1) { g.AddEdge(i, d2); } } for (int j = i + 1; j < s; j++) { if (CheckIntersection(streets[i], streets[j]) == 1) { g.AddEdge(i, j); } } } PathsInfo[] d = new PathsInfo[s + 2]; g.DijkstraShortestPaths(d1, out d); if (double.IsNaN(d[d2].Dist)) { return(false); } Edge[] edges = PathsInfo.ConstructPath(d1, d2, d); for (int i = 0; i < edges.Length - 1; i++) { path.Add(edges[i].To); if (i > 0) { intersections.Add(GetIntersectionPoint(streets[edges[i].From], streets[edges[i].To])); } } return(true); }
/// <summary> /// Wersja III i IV zadania /// Zwraca najkrótszy możliwy czas przejścia przez labirynt z użyciem co najwyżej k lasek dynamitu /// </summary> /// <param name="maze">labirynt</param> /// <param name="k">liczba dostępnych lasek dynamitu, dla wersji III k=1</param> /// <param name="path">zwracana ścieżka</param> /// <param name="t">czas zburzenia ściany</param> /// // pomysl: k+1 takich samych grafow(warstw) bez krawedzi X , a krawedzie X to takie "mosty" miedzy warstwami // ETAP 3 i 4 public int FindShortestPathWithKDynamites(char[,] maze, int k, out string path, int t) { path = ""; int X = maze.GetLength(1); int Y = maze.GetLength(0); int n = X * Y; // liczba punktow w tablicy, dla ulatwienia w szukaniu numeru wierzcholka w innej warstwie grafu int start = -1; int end = -1; AdjacencyListsGraph <AVLAdjacencyList> g = new AdjacencyListsGraph <AVLAdjacencyList>(true, (k + 1) * n); PathsInfo[] p = null; createGraph(maze, g, t, out start, out end, k, n); ShortestPathsGraphExtender.DijkstraShortestPaths(g, start, out p); int[] dist = new int[k + 1]; // tablica najlepszych drog dla uzycia dynamitow od 0 do k for (int i = 0; i <= k; ++i) { dist[i] = -1; } int min = int.MaxValue; int index = 0; // ilosc dynamitow zuzytych dla najlepszej drogi for (int i = 0; i <= k; ++i) // sprawdzenie dla kazdej ilosci uzytego dynamitu najlepszej drogi { int tmp = (int)p[end + i * n].Dist; if (!Double.IsNaN(p[end + i * n].Dist)) // jesli nie NaN to porownac z wartoscia najmniejsza { dist[i] = tmp; if (tmp < min) // jesli nowa wartosc lepsza to zapisac jako min { min = tmp; index = i; } } } if (min == int.MaxValue) { return(-1); // jesli nie znaleziono sciezki to -1 } else { if (X > 1) { path = constructStringE34(PathsInfo.ConstructPath(start, end + index * n, p), X, n); } else // jesli kolejne wierzcholki grafu nie sa na tablicy po prawo/lewo, tylko gora/dol (bo wymiar 1xN czy Nx1 nie wiem) { path = constructStringE341xN(PathsInfo.ConstructPath(start, end + index * n, p), X, n); } return(min); } }
/// <summary> /// Algorytm znajdujący drugą pod względem długości najkrótszą ścieżkę między a i b. /// Możliwe, że jej długość jest równa najkrótszej (jeśli są dwie najkrótsze ścieżki, /// algorytm zwróci jedną z nich). /// Wymagamy, aby na ścieżce nie było powtórzeń wierzchołków ani krawędzi. /// Można założyć, że a!=b oraz że w grafie nie występują pętle. /// </summary> /// <remarks> /// Wymagana złożoność to O(nD), gdzie D jest złożonością implementacji algorytmu Dijkstry w bibliotece Graph. /// </remarks> /// <param name="g"></param> /// <param name="path">null jeśli druga ścieżka nie istnieje, wpp ściezka jako ciąg krawędzi</param> /// <returns>null jeśli druga ścieżka nie istnieje, wpp długość tej ścieżki</returns> public static double?FindSecondSimpleShortestPath(this Graph g, int a, int b, out Edge[] path) { PathsInfo[] pi; PathsInfo[] pi2; Graph G = g.Clone(); G.DijkstraShortestPaths(a, out pi); if (double.IsNaN(pi[b].Dist)) { path = null; return(null); } Edge[] firstPath = PathsInfo.ConstructPath(a, b, pi); double secondShortest = double.PositiveInfinity; path = null; for (int i = firstPath.Length - 1; i >= 0; i--) { G.DelEdge(firstPath[i]); G.DijkstraShortestPaths(a, out pi2); if (double.IsNaN(pi2[b].Dist)) { continue; } if (pi2[b].Dist < secondShortest) { secondShortest = pi2[b].Dist; path = PathsInfo.ConstructPath(a, b, pi2); } G.AddEdge(firstPath[i]); } if (secondShortest == double.PositiveInfinity) { return(null); } return(secondShortest); }
// ETAP 1 i 2 public int FindShortestPath(char[,] maze, bool withDynamite, out string path, int t = 0) { path = ""; int X = maze.GetLength(1); int Y = maze.GetLength(0); int start = -1; int end = -1; AdjacencyListsGraph <AVLAdjacencyList> g = new AdjacencyListsGraph <AVLAdjacencyList>(true, X * Y); PathsInfo[] p = null; if (t == 0) { createGraph(maze, g, out start, out end); } else { createGraph(maze, g, t, out start, out end); } ShortestPathsGraphExtender.DijkstraShortestPaths(g, start, out p); int dist = (int)p[end].Dist; if (!Double.IsNaN(p[end].Dist)) { if (X > 1) { path = constructString(PathsInfo.ConstructPath(start, end, p)); } else { path = constructString1xN(PathsInfo.ConstructPath(start, end, p)); } return(dist); } else { return(-1); } }
/// <summary> /// Algorytm znajdujący drugą pod względem długości najkrótszą ścieżkę między a i b. /// Możliwe, że jej długość jest równa najkrótszej (jeśli są dwie najkrótsze ścieżki, /// algorytm zwróci jedną z nich). /// Wymagamy, aby na ścieżce nie było powtórzeń wierzchołków ani krawędzi. /// Można założyć, że a!=b oraz że w grafie nie występują pętle. /// </summary> /// <remarks> /// Wymagana złożoność to O(nD), gdzie D jest złożonością implementacji algorytmu Dijkstry w bibliotece Graph. /// </remarks> /// <param name="g"></param> /// <param name="path">null jeśli druga ścieżka nie istnieje, wpp ściezka jako ciąg krawędzi</param> /// <returns>null jeśli druga ścieżka nie istnieje, wpp długość tej ścieżki</returns> public static double?FindSecondSimpleShortestPath(this Graph g, int a, int b, out Edge[] path) { PathsInfo[] d; g.DijkstraShortestPaths(a, out d); Edge[] shortestPath = PathsInfo.ConstructPath(a, b, d); if (shortestPath == null) { path = null; return(null); } double minDist = double.MaxValue; bool changed = false; Edge[] temp = null; foreach (Edge e in shortestPath) { PathsInfo[] dT; Graph tG = g.Clone(); tG.DelEdge(e); tG.DijkstraShortestPaths(a, out dT); Edge[] secondShortest = PathsInfo.ConstructPath(a, b, dT); if (dT[b].Dist <= minDist) { changed = true; minDist = dT[b].Dist; temp = PathsInfo.ConstructPath(a, b, dT); } } if (!changed) { path = null; return(null); } path = temp; return(minDist); }
/// <summary> /// Wersje zadania I oraz II /// Zwraca najkrótszy możliwy czas przejścia przez labirynt bez dynamitów lub z dowolną ich liczbą /// </summary> /// <param name="maze">labirynt</param> /// <param name="withDynamite">informacja, czy dostępne są dynamity /// Wersja I zadania -> withDynamites = false, Wersja II zadania -> withDynamites = true</param> /// <param name="path">zwracana ścieżka</param> /// <param name="t">czas zburzenia ściany (dotyczy tylko wersji II)</param> public int FindShortestPath(char[,] maze, bool withDynamite, out string path, int t = 0) { length = maze.GetLength(0); width = maze.GetLength(1); int nVertices = length * width; int start = 0, end = 0; Graph g = new AdjacencyListsGraph <AVLAdjacencyList>(true, nVertices); for (int i = 0; i < length; i++) { for (int j = 0; j < width; j++) { int v = ij2v(i, j); if (maze[i, j] == 'S') { start = v; } if (maze[i, j] == 'E') { end = v; } if (withDynamite) { if (i + 1 < length && maze[i + 1, j] != 'X') { g.AddEdge(v, ij2v(i + 1, j), 1); } else if (i + 1 < length) { g.AddEdge(v, ij2v(i + 1, j), t); } if (j + 1 < width && maze[i, j + 1] != 'X') { g.AddEdge(v, ij2v(i, j + 1), 1); } else if (j + 1 < width) { g.AddEdge(v, ij2v(i, j + 1), t); } if (i - 1 >= 0 && maze[i - 1, j] != 'X') { g.AddEdge(v, ij2v(i - 1, j), 1); } else if (i - 1 >= 0) { g.AddEdge(v, ij2v(i - 1, j), t); } if (j - 1 >= 0 && maze[i, j - 1] != 'X') { g.AddEdge(v, ij2v(i, j - 1), 1); } else if (j - 1 >= 0) { g.AddEdge(v, ij2v(i, j - 1), t); } } else { if (maze[i, j] != 'X') { if (i + 1 < length && maze[i + 1, j] != 'X') { g.AddEdge(v, ij2v(i + 1, j), 1); g.AddEdge(ij2v(i + 1, j), v, 1); } if (j + 1 < width && maze[i, j + 1] != 'X') { g.AddEdge(ij2v(i, j + 1), v, 1); g.AddEdge(v, ij2v(i, j + 1), 1); } } } } } g.DijkstraShortestPaths(start, out PathsInfo[] d); if ((int)d[end].Dist == int.MinValue) { path = ""; return(-1); } Edge[] pathEdges = PathsInfo.ConstructPath(start, end, d); StringBuilder builder = new StringBuilder(); (int x, int y) = v2ij(pathEdges[0].From); foreach (var e in pathEdges) { (int xEnd, int yEnd) = v2ij(e.To); if (xEnd == x + 1) { builder.Append('S'); } if (xEnd == x - 1) { builder.Append('N'); } if (yEnd == y + 1) { builder.Append('E'); } if (yEnd == y - 1) { builder.Append('W'); } x = xEnd; y = yEnd; } path = builder.ToString(); // tej linii na laboratorium nie zmieniamy! return((int)d[end].Dist); }
/// <summary> /// /// </summary> /// <param name="taskGraph">Graf opisujący zależności procedur</param> /// <param name="taskTimes">Tablica długości czasów procedur</param> /// <param name="startTimes">Parametr wyjśćiowy z najpóźniejszymi możliwymi startami procedur przy optymalnym czasie całości</param> /// <param name="startTimes">Parametr wyjśćiowy z dowolna wybraną ścieżką krytyczną</param> /// <returns>Najkrótszy czas w jakim można wykonać cały program</returns> public double CalculateTimesLatestPossible(Graph taskGraph, double[] taskTimes, out double[] startTimes, out int[] criticalPath) { startTimes = null; criticalPath = null; int n = taskGraph.VerticesCount; bool allIsolated = true; int maxV = 0; double maxVal = 0; for (int i = 0; i < n; i++) { if (taskGraph.OutEdges(i).Count() > 0) { allIsolated = false; break; } if (maxVal < taskTimes[i]) { maxV = i; maxVal = taskTimes[i]; } } if (allIsolated) { startTimes = new double[n]; for (int i = 0; i < n; i++) { startTimes[i] = maxVal - taskTimes[i]; } criticalPath = new int[] { maxV }; return(maxVal); } var startingVertices = new List <int>(); for (int i = 0; i < n; i++) { if (taskGraph.InDegree(i) == 0) { startingVertices.Add(i); } } var endingVertices = new List <int>(); for (int i = 0; i < n; i++) { if (taskGraph.OutDegree(i) == 0) { endingVertices.Add(i); } } var reversedGraph = taskGraph.Reverse(); Graph g = taskGraph.IsolatedVerticesGraph(true, n + 2); for (int i = 0; i < n; i++) { foreach (var v in reversedGraph.OutEdges(i)) { g.AddEdge(i, v.To, -taskTimes[i]); } } //n - zrodlo //n+1 ujscie foreach (var i in endingVertices) { g.AddEdge(n, i, 0); } foreach (var i in startingVertices) { g.AddEdge(i, n + 1, -taskTimes[i]); } g.DAGShortestPaths(n, out PathsInfo[] d); double programTime = -d[n + 1].Dist; startTimes = new double[n]; for (int i = 0; i < n; i++) { startTimes[i] = programTime + d[i].Dist - taskTimes[i]; } int startigVertexForCriticalPath = 0; var longestPath = PathsInfo.ConstructPath(n, n + 1, d); startigVertexForCriticalPath = longestPath[longestPath.Length - 1].From; Edge[] path = PathsInfo.ConstructPath(n, startigVertexForCriticalPath, d); criticalPath = new int[path.Length]; for (int i = 0; i < criticalPath.Length; i++) { criticalPath[i] = path[i].To; } Array.Reverse(criticalPath); return(-d[n + 1].Dist); }
/// <summary> /// Wersje zadania I oraz II /// Zwraca najkrótszy możliwy czas przejścia przez labirynt bez dynamitów lub z dowolną ich liczbą /// </summary> /// <param name="maze">labirynt</param> /// <param name="withDynamite">informacja, czy dostępne są dynamity /// Wersja I zadania -> withDynamites = false, Wersja II zadania -> withDynamites = true</param> /// <param name="path">zwracana ścieżka</param> /// <param name="t">czas zburzenia ściany (dotyczy tylko wersji II)</param> public int FindShortestPath(char[,] maze, bool withDynamite, out string path, int t = 0) { //path = null; // tej linii na laboratorium nie zmieniamy! int totalRows = maze.GetLength(0); int totalColumns = maze.GetLength(1); int totalFields = totalRows * totalColumns; int startVertex = -1; int endVertex = -1; HashSet <char> goodChars = new HashSet <char>() { 'O', 'S', 'E' }; Graph g = new AdjacencyListsGraph <HashTableAdjacencyList>(true, totalFields); for (int row = 0; row < totalRows; row++) { for (int column = 0; column < totalColumns; column++) { //if (maze[row, column] == 'X') // continue; if (maze[row, column] == 'S') { startVertex = GetVertexNumber(row, column); } if (maze[row, column] == 'E') { endVertex = GetVertexNumber(row, column); } int currentVertexNumber = GetVertexNumber(row, column); if (row > 0) { int otherVertexNumber = GetVertexNumber(row - 1, column); if (goodChars.Contains(maze[row - 1, column])) { g.AddEdge(currentVertexNumber, otherVertexNumber); } if (maze[row - 1, column] == 'X' && t != 0) { g.AddEdge(currentVertexNumber, otherVertexNumber, t); } } if (row < totalRows - 1) { int otherVertexNumber = GetVertexNumber(row + 1, column); if (goodChars.Contains(maze[row + 1, column])) { g.AddEdge(currentVertexNumber, otherVertexNumber); } if (maze[row + 1, column] == 'X' && t != 0) { g.AddEdge(currentVertexNumber, otherVertexNumber, t); } } if (column > 0) { int otherVertexNumber = GetVertexNumber(row, column - 1); if (goodChars.Contains(maze[row, column - 1])) { g.AddEdge(currentVertexNumber, otherVertexNumber); } if (maze[row, column - 1] == 'X' && t != 0) { g.AddEdge(currentVertexNumber, otherVertexNumber, t); } } if (column < totalColumns - 1) { int otherVertexNumber = GetVertexNumber(row, column + 1); if (goodChars.Contains(maze[row, column + 1])) { g.AddEdge(currentVertexNumber, otherVertexNumber); } if (maze[row, column + 1] == 'X' && t != 0) { g.AddEdge(currentVertexNumber, otherVertexNumber, t); } } } } bool result = g.DijkstraShortestPaths(startVertex, out PathsInfo[] pathsInfo); double length = pathsInfo[endVertex].Dist; if (double.IsNaN(length)) { path = ""; return(-1); } else { Edge[] pathEdges = PathsInfo.ConstructPath(startVertex, endVertex, pathsInfo); StringBuilder pathStringBuilder = new StringBuilder(); foreach (var edge in pathEdges) { pathStringBuilder.Append(ConvertEdgeToChar(edge)); } path = pathStringBuilder.ToString(); return((int)length); } //-------------------------------------------------------- int GetVertexNumber(int row, int column) { return(row * totalColumns + column); } char ConvertEdgeToChar(Edge edge) { var startField = GetRowAndColumn(edge.From); var endField = GetRowAndColumn(edge.To); if (startField.row == endField.row) { if (endField.column > startField.column) { return('E'); } else if (endField.column < startField.column) { return('W'); } } else if (startField.column == endField.column) { if (endField.row > startField.row) { return('S'); } else if (endField.row < startField.row) { return('N'); } } throw new ArgumentException("Incorrect edge"); } Field GetRowAndColumn(int vertexNumber) { int row = vertexNumber / totalColumns; int column = vertexNumber - row * totalColumns; return(new Field(row, column)); } }
/// <summary> /// Wersja III i IV zadania /// Zwraca najkrótszy możliwy czas przejścia przez labirynt z użyciem co najwyżej k lasek dynamitu /// </summary> /// <param name="maze">labirynt</param> /// <param name="k">liczba dostępnych lasek dynamitu, dla wersji III k=1</param> /// <param name="path">zwracana ścieżka</param> /// <param name="t">czas zburzenia ściany</param> public int FindShortestPathWithKDynamites(char[,] maze, int k, out string path, int t) { path = ""; // tej linii na laboratorium nie zmieniamy! int totalRows = maze.GetLength(0); int totalColumns = maze.GetLength(1); int totalFields = totalRows * totalColumns; int[] startVerices = new int[k + 1]; int[] endVertices = new int[k + 1]; for (int i = 0; i < startVerices.Length; i++) { startVerices[i] = endVertices[i] = -1; } HashSet <char> goodChars = new HashSet <char>() { 'O', 'S', 'E' }; Graph g = new AdjacencyListsGraph <HashTableAdjacencyList>(true, totalFields * (k + 1)); for (int row = 0; row < totalRows; row++) { for (int column = 0; column < totalColumns; column++) { if (maze[row, column] == 'S') { for (int layerNumber = 0; layerNumber < startVerices.Length; layerNumber++) { startVerices[layerNumber] = GetVertexNumber(row, column, layerNumber); } } if (maze[row, column] == 'E') { for (int layerNumber = 0; layerNumber < endVertices.Length; layerNumber++) { endVertices[layerNumber] = GetVertexNumber(row, column, layerNumber); } } int[] currentVerticesNumbers = new int[k + 1]; int[] otherVerticesNumbers = new int[k + 1]; for (int layerNumber = 0; layerNumber < currentVerticesNumbers.Length; layerNumber++) { currentVerticesNumbers[layerNumber] = GetVertexNumber(row, column, layerNumber); } if (row > 0) { for (int layerNumber = 0; layerNumber < otherVerticesNumbers.Length; layerNumber++) { otherVerticesNumbers[layerNumber] = GetVertexNumber(row - 1, column, layerNumber); } if (goodChars.Contains(maze[row - 1, column])) { for (int layerNumber = 0; layerNumber < currentVerticesNumbers.Length; layerNumber++) { g.AddEdge(currentVerticesNumbers[layerNumber], otherVerticesNumbers[layerNumber]); } } if (maze[row - 1, column] == 'X' && t != 0) { for (int layerNumber = 0; layerNumber < k; layerNumber++) { g.AddEdge(currentVerticesNumbers[layerNumber], otherVerticesNumbers[layerNumber + 1], t); } } } if (row < totalRows - 1) { for (int layerNumber = 0; layerNumber < otherVerticesNumbers.Length; layerNumber++) { otherVerticesNumbers[layerNumber] = GetVertexNumber(row + 1, column, layerNumber); } if (goodChars.Contains(maze[row + 1, column])) { for (int layerNumber = 0; layerNumber < currentVerticesNumbers.Length; layerNumber++) { g.AddEdge(currentVerticesNumbers[layerNumber], otherVerticesNumbers[layerNumber]); } } if (maze[row + 1, column] == 'X' && t != 0) { for (int layerNumber = 0; layerNumber < k; layerNumber++) { g.AddEdge(currentVerticesNumbers[layerNumber], otherVerticesNumbers[layerNumber + 1], t); } } } if (column > 0) { for (int layerNumber = 0; layerNumber < otherVerticesNumbers.Length; layerNumber++) { otherVerticesNumbers[layerNumber] = GetVertexNumber(row, column - 1, layerNumber); } if (goodChars.Contains(maze[row, column - 1])) { for (int layerNumber = 0; layerNumber < currentVerticesNumbers.Length; layerNumber++) { g.AddEdge(currentVerticesNumbers[layerNumber], otherVerticesNumbers[layerNumber]); } } if (maze[row, column - 1] == 'X' && t != 0) { for (int layerNumber = 0; layerNumber < k; layerNumber++) { g.AddEdge(currentVerticesNumbers[layerNumber], otherVerticesNumbers[layerNumber + 1], t); } } } if (column < totalColumns - 1) { for (int layerNumber = 0; layerNumber < otherVerticesNumbers.Length; layerNumber++) { otherVerticesNumbers[layerNumber] = GetVertexNumber(row, column + 1, layerNumber); } if (goodChars.Contains(maze[row, column + 1])) { for (int layerNumber = 0; layerNumber < currentVerticesNumbers.Length; layerNumber++) { g.AddEdge(currentVerticesNumbers[layerNumber], otherVerticesNumbers[layerNumber]); } } if (maze[row, column + 1] == 'X' && t != 0) { for (int layerNumber = 0; layerNumber < k; layerNumber++) { g.AddEdge(currentVerticesNumbers[layerNumber], otherVerticesNumbers[layerNumber + 1], t); } } } } } for (int layerNumber = 0; layerNumber < k; layerNumber++) { g.AddEdge(endVertices[layerNumber], endVertices[layerNumber + 1], 0); g.AddEdge(endVertices[layerNumber + 1], endVertices[layerNumber], 0); } bool result = g.DijkstraShortestPaths(startVerices[0], out PathsInfo[] pathsInfo); double length = pathsInfo[endVertices[k]].Dist; if (double.IsNaN(length)) { path = ""; return(-1); } else { Edge[] pathEdges = PathsInfo.ConstructPath(startVerices[0], endVertices[k], pathsInfo); StringBuilder pathStringBuilder = new StringBuilder(); foreach (var edge in pathEdges) { char direction = ConvertEdgeToChar(edge); if (direction != '*') { pathStringBuilder.Append(direction); } } path = pathStringBuilder.ToString(); return((int)length); } //Edge[] pathEdges = PathsInfo.ConstructPath(startVertex, endVertex, pathsInfo); //StringBuilder pathStringBuilder = new StringBuilder(); //foreach (var edge in pathEdges) //{ // pathStringBuilder.Append(ConvertEdgeToChar(edge)); //} //path = pathStringBuilder.ToString(); //if (true) //{ //} //return double.IsNaN(length) ? -1 : (int)length; int GetVertexNumber(int row, int column, int layerNumber) { return(row * totalColumns + column + layerNumber * totalFields); } char ConvertEdgeToChar(Edge edge) { var startField = GetRowAndColumn(edge.From); var endField = GetRowAndColumn(edge.To); if (startField.row == endField.row) { if (endField.column > startField.column) { return('E'); } else if (endField.column < startField.column) { return('W'); } else if (endField.column == startField.column) { return('*'); } } else if (startField.column == endField.column) { if (endField.row > startField.row) { return('S'); } else if (endField.row < startField.row) { return('N'); } } throw new ArgumentException("Incorrect edge"); } Field GetRowAndColumn(int vertexNumber) { int layerNumber = vertexNumber / totalFields; int row = (vertexNumber - layerNumber * totalFields) / totalColumns; int column = (vertexNumber - layerNumber * totalFields) - row * totalColumns; return(new Field(row, column)); } }
/// <summary> /// Sprawdza możliwość przejazdu między dzielnicami-wielokątami district1 i district2, /// tzn. istnieją para ulic, pomiędzy którymi jest przejazd /// oraz fragment jednej ulicy należy do obszaru jednej z dzielnic i fragment drugiej należy do obszaru drugiej dzielnicy /// </summary> /// <returns>Informacja czy istnieje przejazd między dzielnicami</returns> // etap 4 public bool CheckDistricts(Street[] streets, Point[] district1, Point[] district2, out List <int> path, out List <Point> intersections) { path = new List <int>(); intersections = new List <Point>(); int S = streets.Length; int n1 = district1.Length; int n2 = district2.Length; Graph g = new AdjacencyListsGraph <SimpleAdjacencyList>(false, S + 2); // 0..S-1 streets S == district1, S + 1 == district2 for (int i = 0; i < S; ++i) { // ulica wchodzi do district1 for (int k = 0; k < n1; ++k) { if (CheckIntersection(streets[i], new Street(district1[k % n1], district1[(k + 1) % n1])) != 0) { g.AddEdge(i, S); } } // ulica wchodzi do district2 for (int k = 0; k < n2; ++k) { if (CheckIntersection(streets[i], new Street(district2[k % n2], district2[(k + 1) % n2])) != 0) { g.AddEdge(i, S + 1); } } // ulica krzyzuje sie z kolejna ulica if (i != S - 1) { for (int j = i + 1; j < S; ++j) { if (CheckIntersection(streets[i], streets[j]) != 0) { g.AddEdge(i, j); } } } } PathsInfo[] p = new PathsInfo[S + 2]; ShortestPathsGraphExtender.DijkstraShortestPaths(g, S, out p); Edge[] e = PathsInfo.ConstructPath(S, S + 1, p); // jesli istnieje sciezka od S do S+1 if (!Double.IsNaN(p[S + 1].Dist)) { path = new List <int>(); // pododawac kolejne pkty (bez S i bez S+1) for (int j = 0; j < e.Length - 1; ++j) { path.Add(e[j].To); } intersections = new List <Point>(); // przeciecia kolejnych ulic for (int j = 0; j < path.Count - 1; ++j) { intersections.Add(GetIntersectionPoint(streets[path[j]], streets[path[j + 1]])); } return(true); } else { return(false); } }
public (int c1, int c2, int?bypass, double time, Edge[] path)? FindBestCitiesPair(Graph times, double[] passThroughCityTimes, int[] nominatedCities, bool buildBypass) { int n = times.VerticesCount; int c1 = -1, c2 = -1; Graph g = times.IsolatedVerticesGraph(true, n); for (int i = 0; i < n; i++) { foreach (var e in times.OutEdges(i)) { g.AddEdge(e.From, e.To, e.Weight + passThroughCityTimes[e.From]); } } PathsInfo[] opt = null; PathsInfo[][] sciezki = new PathsInfo[nominatedCities.Length][]; double dystans = double.MaxValue; for (int i = 0; i < nominatedCities.Length; i++) { foreach (var e in g.OutEdges(nominatedCities[i])) { g.ModifyEdgeWeight(e.From, e.To, -passThroughCityTimes[nominatedCities[i]]); } PathsInfo[] pi; g.DijkstraShortestPaths(nominatedCities[i], out pi); sciezki[i] = pi; for (int k = 0; k < nominatedCities.Length; k++) { if (k == i) { continue; } if (GraphHelperExtender.IsNaN(pi[nominatedCities[k]].Dist) == true) { continue; } if (pi[nominatedCities[k]].Dist < dystans) { dystans = pi[nominatedCities[k]].Dist; c1 = nominatedCities[i]; c2 = nominatedCities[k]; opt = pi; } } foreach (var e in g.OutEdges(nominatedCities[i])) { g.ModifyEdgeWeight(e.From, e.To, passThroughCityTimes[nominatedCities[i]]); } } if (dystans == double.MaxValue) { return(null); } Edge[] sciezka = PathsInfo.ConstructPath(c1, c2, opt); if (buildBypass == false) { return(c1, c2, null, dystans, sciezka); } //ETAP 2 int obwodnica = -1; PathsInfo p1 = new PathsInfo(); PathsInfo p2 = new PathsInfo(); for (int i = 0; i < nominatedCities.Length; i++) { for (int j = 0; j < nominatedCities.Length; j++) { if (i == j) { continue; } for (int k = 0; k < n; k++) { if (k == nominatedCities[i] || k == nominatedCities[j]) { continue; } if (sciezki[i][k].Dist + sciezki[j][k].Dist < dystans) { dystans = sciezki[i][k].Dist + sciezki[j][k].Dist; obwodnica = k; //sciezki[i][k].Dist -= passThroughCityTimes[k]; c1 = nominatedCities[i]; c2 = nominatedCities[j]; p1 = sciezki[i][k]; p2 = sciezki[j][k]; //p1 = sciezki[i]; //sciezki[i][k].Dist += passThroughCityTimes[k]; } } } } if (obwodnica == -1) { return(c1, c2, null, dystans, sciezka); } //sciezka = PathsInfo.ConstructPath(c1, c2,p1); int indc1 = -1; int indc2 = -1; for (int i = 0; i < n; i++) { if (nominatedCities[i] == c1) { indc1 = i; break; } } for (int j = 0; j < n; j++) { if (nominatedCities[j] == c2) { indc2 = j; break; } } List <Edge> sciezkapom = new List <Edge>(); sciezkapom.Add((Edge)p1.Last); int ostatniwierzch = ((Edge)p1.Last).From; while (ostatniwierzch != c1) { sciezkapom.Add((Edge)sciezki[indc1][ostatniwierzch].Last); ostatniwierzch = ((Edge)sciezki[indc1][ostatniwierzch].Last).From; } sciezkapom.Reverse(); sciezkapom.Add(new Edge(((Edge)p2.Last).To, ((Edge)p2.Last).From, ((Edge)p2.Last).Weight)); ostatniwierzch = ((Edge)p2.Last).From; while (ostatniwierzch != c2) { sciezkapom.Add(new Edge(((Edge)sciezki[indc2][ostatniwierzch].Last).To, ((Edge)sciezki[indc2][ostatniwierzch].Last).From, ((Edge)sciezki[indc2][ostatniwierzch].Last).Weight)); ostatniwierzch = ((Edge)sciezki[indc2][ostatniwierzch].Last).From; } sciezka = sciezkapom.ToArray(); return(c1, c2, obwodnica, dystans, sciezka); }
/// <summary> /// Wersje zadania I oraz II /// Zwraca najkrótszy możliwy czas przejścia przez labirynt bez dynamitów lub z dowolną ich liczbą /// </summary> /// <param name="maze">labirynt</param> /// <param name="withDynamite">informacja, czy dostępne są dynamity /// Wersja I zadania -> withDynamites = false, Wersja II zadania -> withDynamites = true</param> /// <param name="path">zwracana ścieżka</param> /// <param name="t">czas zburzenia ściany (dotyczy tylko wersji II)</param> public int FindShortestPath(char[,] maze, bool withDynamite, out string path, int t = 0) { int x = maze.GetLength(1); int y = maze.GetLength(0); int vNo = 0; int start = 0; int end = 0; Graph g = new AdjacencyListsGraph <AVLAdjacencyList>(true, x * y); for (int i = 0; i < y; i++) { for (int j = 0; j < x; j++) { //Get the neighbours, if it tries to go out of bounds set the neighbor to 'N' char curChar = maze[i, j]; char left = j >= 1 ? maze[i, j - 1] : 'N'; char right = j < x - 1 ? maze[i, j + 1] : 'N'; char up = i >= 1 ? maze[i - 1, j] : 'N'; char down = i < y - 1 ? maze[i + 1, j] : 'N'; //If it comes across S or E, mark their vNo's (vertices number) if (curChar == 'S') { start = vNo; } if (curChar == 'E') { end = vNo; } //Go through each neighbour and add proper edges if applicable switch (up) { case 'N': break; case 'X': if (withDynamite) { g.AddEdge(vNo, vNo - x, t); } break; default: g.AddEdge(vNo, vNo - x); break; } switch (down) { case 'N': break; case 'X': if (withDynamite) { g.AddEdge(vNo, vNo + x, t); } break; default: g.AddEdge(vNo, vNo + x); break; } switch (left) { case 'N': break; case 'X': if (withDynamite) { g.AddEdge(vNo, vNo - 1, t); } break; default: g.AddEdge(vNo, vNo - 1); break; } switch (right) { case 'N': break; case 'X': if (withDynamite) { g.AddEdge(vNo, vNo + 1, t); } break; default: g.AddEdge(vNo, vNo + 1); break; } vNo++; } } path = ""; PathsInfo[] paths; if (!g.DijkstraShortestPaths(start, out paths)) { return(-1); } if (paths[end].Last == null) { return(-1); } Edge[] edgePath = PathsInfo.ConstructPath(start, end, paths); StringBuilder str = new StringBuilder(); for (int i = 0; i < edgePath.Length; i++) { int curXFrom = edgePath[i].From / x; int curYFrom = edgePath[i].From - x * curXFrom; int curXTo = edgePath[i].To / x; int curYTo = edgePath[i].To - x * curXTo; if (curXFrom == curXTo) { if (curYFrom < curYTo) { str.Append('E'); } else { str.Append('W'); } } else { if (curXFrom < curXTo) { str.Append('S'); } else { str.Append('N'); } } } path = str.ToString(); return((int)paths[end].Dist); }
/// <summary> /// Algorytm znajdujący drugą pod względem długości najkrótszą ścieżkę między a i b. /// Możliwe, że jej długość jest równa najkrótszej (jeśli są dwie najkrótsze ścieżki, /// algorytm zwróci jedną z nich). /// Dopuszczamy, aby na ścieżce powtarzały się wierzchołki/krawędzie. /// Można założyć, że a!=b oraz że w grafie nie występują pętle. /// </summary> /// <remarks> /// Wymagana złożoność do O(D), gdzie D jest złożonością implementacji alogorytmu Dijkstry w bibliotece Graph. /// </remarks> /// <param name="g"></param> /// <param name="path">null jeśli druga ścieżka nie istnieje, wpp ściezka jako ciąg krawędzi</param> /// <returns>null jeśli druga ścieżka nie istnieje, wpp długość znalezionej ścieżki</returns> public static double?FindSecondShortestPath(this Graph g, int a, int b, out Edge[] path) { PathsInfo[] pi; PathsInfo[] pi2; Graph G = g.Clone(); G.DijkstraShortestPaths(a, out pi); if (double.IsNaN(pi[b].Dist)) { path = null; return(null); } Edge[] firstPath = PathsInfo.ConstructPath(a, b, pi); List <Edge> finalPath = new List <Edge>(); if (G.Directed) { G = G.Reverse(); } G.DijkstraShortestPaths(b, out pi2); if (pi2 == null) { path = null; return(null); } int u = -1, v = -1; double secondShortest = double.PositiveInfinity; for (int i = firstPath.Length - 1; i >= 0; i--) { foreach (Edge ed in g.OutEdges(firstPath[i].From)) { if (ed.To != firstPath[i].To) { double current; if (double.IsNaN(pi2[ed.To].Dist)) { continue; } if ((current = (ed.Weight + pi[firstPath[i].From].Dist + pi2[ed.To].Dist)) < secondShortest) { secondShortest = current; u = firstPath[i].From; v = ed.To; } } } } if (u == -1) { path = null; return(null); } int ind = 0; while (firstPath[ind].From != u) { finalPath.Add(firstPath[ind++]); } Edge[] second = PathsInfo.ConstructPath(b, v, pi2); finalPath.Add(new Edge(u, v, g.GetEdgeWeight(u, v))); ind = v; Edge?last; while ((last = pi2[ind].Last) != null) { Edge e = pi2[ind].Last.Value; finalPath.Add(new Edge(e.To, e.From, e.Weight)); ind = last.Value.From; } path = finalPath.ToArray(); return(secondShortest); }
public (int c1, int c2, int?bypass, double time, Edge[] path)? FindBestCitiesPair(Graph times, double[] passThroughCityTimes, int[] nominatedCities, bool buildBypass) { Graph g = times.IsolatedVerticesGraph(true, times.VerticesCount); int c1 = -1; int c2 = -1; int city1 = -1; int city2 = -1; double best = double.MaxValue; for (int i = 0; i < times.VerticesCount; i++) { foreach (Edge e in times.OutEdges(i)) { g.AddEdge(e.To, e.From, e.Weight + passThroughCityTimes[e.To]); g.AddEdge(e.From, e.To, e.Weight + passThroughCityTimes[e.From]); } } PathsInfo[][] distance = new PathsInfo[nominatedCities.Length][]; for (int i = 0; i < nominatedCities.Length; i++) { foreach (Edge e in g.OutEdges(nominatedCities[i])) { g.ModifyEdgeWeight(e.From, e.To, -passThroughCityTimes[nominatedCities[i]]); } g.DijkstraShortestPaths(nominatedCities[i], out distance[i]); foreach (Edge e in g.OutEdges(nominatedCities[i])) { g.ModifyEdgeWeight(e.From, e.To, passThroughCityTimes[nominatedCities[i]]); } for (int j = 0; j < nominatedCities.Length; j++) { if (i == j) { continue; } if (distance[i][nominatedCities[j]].Dist.IsNaN()) { continue; } if ((distance[i][nominatedCities[j]].Dist < best)) { best = distance[i][nominatedCities[j]].Dist; c1 = nominatedCities[i]; c2 = nominatedCities[j]; city1 = i; city2 = j; } } } if (best == double.MaxValue) { return(null); } Edge[] path = PathsInfo.ConstructPath(c1, c2, distance[city1]); if (buildBypass == false) { return(c1, c2, null, best, path); } PathsInfo firstPart = new PathsInfo(); PathsInfo secondPart = new PathsInfo(); int passedVertex = -1; for (int i = 0; i < nominatedCities.Length; i++) { for (int j = 0; j < nominatedCities.Length; j++) { if (i == j) { continue; } for (int z = 0; z < times.VerticesCount; z++) { if (z == nominatedCities[i] || z == nominatedCities[j]) { continue; } if (distance[i][z].Dist.IsNaN() || distance[j][z].Dist.IsNaN()) { continue; } if (distance[i][z].Dist + distance[j][z].Dist < best) { passedVertex = z; firstPart = distance[i][z]; secondPart = distance[j][z]; best = distance[i][z].Dist + distance[j][z].Dist; c1 = nominatedCities[i]; c2 = nominatedCities[j]; city1 = i; city2 = j; } } } } if (passedVertex == -1) { return(c1, c2, null, best, path); } List <Edge> tmp = new List <Edge>(); List <Edge> tmp2 = new List <Edge>(); tmp.Add((Edge)firstPart.Last); int prev = ((Edge)firstPart.Last).From; while (prev != c1) { tmp.Add((Edge)distance[city1][prev].Last); prev = ((Edge)distance[city1][prev].Last).From; } tmp2.Add(new Edge(((Edge)secondPart.Last).To, ((Edge)secondPart.Last).From, ((Edge)secondPart.Last).Weight)); prev = ((Edge)secondPart.Last).From; while (prev != c2) { tmp2.Add(new Edge(((Edge)distance[city2][prev].Last).To, ((Edge)distance[city2][prev].Last).From, ((Edge)distance[city2][prev].Last).Weight)); prev = ((Edge)distance[city2][prev].Last).From; } tmp.Reverse(); foreach (Edge e in tmp2) { tmp.Add(e); } return(c1, c2, passedVertex, best, tmp.ToArray()); }
public (int c1, int c2, int?bypass, double time, Edge[] path)? FindBestCitiesPair(Graph times, double[] passThroughCityTimes, int[] nominatedCities, bool buildBypass) { var g_cloned = times.Clone(); int n = g_cloned.VerticesCount; for (int v = 0; v < n; v++) { foreach (Edge e in g_cloned.OutEdges(v)) { g_cloned.ModifyEdgeWeight(v, e.To, passThroughCityTimes[v] / 2); } } if (!buildBypass) { int minCity1 = int.MaxValue; int minCity2 = int.MaxValue; double time = double.MaxValue; PathsInfo[] tmpTmpPathInfo = { }; PathsInfo[] tmpPathInfo = { }; Edge[] shortestPath = { }; for (int i = 0; i < nominatedCities.Length; i++) //foreach (var start in nominatedCities) { int start = nominatedCities[i]; double minDistTmp = double.MaxValue; int minCityTmp = -1; Edge[] shortestPathTmp = { }; g_cloned.DijkstraShortestPaths(start, out PathsInfo[] d); for (int j = i; j < nominatedCities.Length; j++) //foreach (var end in nominatedCities) { int end = nominatedCities[j]; if (start != end) { d[end].Dist -= passThroughCityTimes[end] / 2 + passThroughCityTimes[start] / 2; if (d[end].Dist < time && d[end].Dist < minDistTmp) { minDistTmp = d[end].Dist; minCityTmp = end; tmpTmpPathInfo = d; } } } if (minCityTmp != -1) { if (time > minDistTmp) { time = minDistTmp; minCity1 = start; minCity2 = minCityTmp; tmpPathInfo = tmpTmpPathInfo; } } } if (time == double.MaxValue) { return(null); } else { shortestPath = PathsInfo.ConstructPath(minCity1, minCity2, tmpPathInfo); } return(minCity1, minCity2, null, time, shortestPath); } else /*budujemy obwodnicę*/ { int minCity1 = int.MaxValue; int minCity2 = int.MaxValue; double time = double.MaxValue; Edge[] shortestPath = { }; int byPassCity = -1; PathsInfo[] tmpPathInfo = { }; int i1 = -1; int j1 = -1; //var pathsToAllVertices = new HashTable<int, PathsInfo[]>(); var pathsToAllVerticesTab = new PathsInfo[nominatedCities.Length][]; for (int i = 0; i < nominatedCities.Length; i++) { int start = nominatedCities[i]; g_cloned.DijkstraShortestPaths(start, out PathsInfo[] d); //pathsToAllVertices.Insert(start, d); pathsToAllVerticesTab[i] = d; for (int j = i; j < nominatedCities.Length; j++) { int end = nominatedCities[j]; if (start != end) { d[end].Dist -= passThroughCityTimes[end] / 2 + passThroughCityTimes[start] / 2; if (d[end].Dist < time) { time = d[end].Dist; minCity1 = start; minCity2 = end; tmpPathInfo = d; i1 = i; j1 = j; } } } } for (int i = 0; i < nominatedCities.Length; i++) { int start = nominatedCities[i]; for (int j = i; j < nominatedCities.Length; j++) { int end = nominatedCities[j]; for (int ii = 0; ii < g_cloned.VerticesCount; ii++) { if (start != end && ii != start && ii != end) { double timeThroughICity = pathsToAllVerticesTab[i][ii].Dist + pathsToAllVerticesTab[j][ii].Dist - passThroughCityTimes[ii] - 0.5 * passThroughCityTimes[start] - 0.5 * passThroughCityTimes[end]; if (timeThroughICity < time && timeThroughICity >= 0) { byPassCity = ii; time = timeThroughICity; minCity1 = start; minCity2 = end; i1 = i; j1 = j; } } } } } if (time == double.MaxValue) { return(null); } else if (byPassCity != -1) { var path1 = PathsInfo.ConstructPath(minCity1, byPassCity, pathsToAllVerticesTab[i1]); var path2 = PathsInfo.ConstructPath(minCity2, byPassCity, pathsToAllVerticesTab[j1]); Array.Reverse(path2); for (int i = 0; i < path2.Length; i++) { path2[i] = new Edge(path2[i].To, path2[i].From); } shortestPath = new Edge[path1.Length + path2.Length]; Array.Copy(path1, shortestPath, path1.Length); Array.Copy(path2, 0, shortestPath, path1.Length, path2.Length); } else { shortestPath = PathsInfo.ConstructPath(minCity1, minCity2, tmpPathInfo); } if (byPassCity == -1) { return(minCity1, minCity2, null, time, shortestPath); } return(minCity1, minCity2, byPassCity, time, shortestPath); } }
/// <summary> /// Sprawdza możliwość przejazdu między dzielnicami-wielokątami district1 i district2, /// tzn. istnieją para ulic, pomiędzy którymi jest przejazd /// oraz fragment jednej ulicy należy do obszaru jednej z dzielnic i fragment drugiej należy do obszaru drugiej dzielnicy /// </summary> /// <returns>Informacja czy istnieje przejazd między dzielnicami</returns> public bool CheckDistricts(Street[] streets, Point[] district1, Point[] district2, out List <int> path, out List <Point> intersections) { int n = streets.Length; Graph g = new AdjacencyListsGraph <HashTableAdjacencyList>(false, n + 2); int source = n; int target = n + 1; for (int i = 0; i < n; i++) { for (int j = 0; j < district1.Length; j++) { int result = CheckIntersection(streets[i], new Street(district1[j], district1[(j + 1) % district1.Length])); if (result == 1 || result == int.MaxValue) { g.AddEdge(source, i); } } for (int j = 0; j < district2.Length; j++) { int result = CheckIntersection(streets[i], new Street(district2[j], district2[(j + 1) % district2.Length])); if (result == 1 || result == int.MaxValue) { g.AddEdge(i, target); } } for (int j = i + 1; j < n; j++) { int result = CheckIntersection(streets[i], streets[j]); if (result == 1) { g.AddEdge(i, j); } else if (result == int.MaxValue) { throw new ArgumentException(); } } } g.DijkstraShortestPaths(source, out PathsInfo[] d); path = new List <int>(); intersections = new List <Point>(); if (d[target].Dist.IsNaN()) { return(false); } Edge[] edges = PathsInfo.ConstructPath(source, target, d); int previous = -1; for (int i = 0; i < edges.Length - 1; i++) { path.Add(edges[i].To); if (previous != -1) { intersections.Add(GetIntersectionPoint(streets[previous], streets[edges[i].To])); } previous = edges[i].To; } return(true); }
/// <summary> /// Algorytm znajdujący drugą pod względem długości najkrótszą ścieżkę między a i b. /// Możliwe, że jej długość jest równa najkrótszej (jeśli są dwie najkrótsze ścieżki, /// algorytm zwróci jedną z nich). /// Dopuszczamy, aby na ścieżce powtarzały się wierzchołki/krawędzie. /// Można założyć, że a!=b oraz że w grafie nie występują pętle. /// </summary> /// <remarks> /// Wymagana złożoność do O(D), gdzie D jest złożonością implementacji alogorytmu Dijkstry w bibliotece Graph. /// </remarks> /// <param name="g"></param> /// <param name="path">null jeśli druga ścieżka nie istnieje, wpp ściezka jako ciąg krawędzi</param> /// <returns>null jeśli druga ścieżka nie istnieje, wpp długość znalezionej ścieżki</returns> public static double?FindSecondShortestPath(this Graph g, int a, int b, out Edge[] path) { path = null; double?min = Double.MaxValue; PathsInfo[] d; Graph G; Edge edge = new Edge(); int index = 0; if (g.Directed) { G = g.Reverse(); } else { G = g; } G.DijkstraShortestPaths(b, out d); if (d[a].Dist.IsNaN()) { return(null); } Edge[] firstpath = PathsInfo.ConstructPath(b, a, d); for (int i = 0; i < firstpath.Length; i++) { int v = firstpath[i].To; foreach (Edge e in g.OutEdges(v)) { if (e.To != firstpath[i].From && d[e.To].Dist + e.Weight + d[a].Dist - d[v].Dist < min) { min = d[e.To].Dist + e.Weight + d[a].Dist - d[v].Dist; edge = e; index = i; } } } if (min == Double.MaxValue) { return(null); } Edge[] tmp = PathsInfo.ConstructPath(b, edge.To, d); Array.Reverse(tmp); for (int i = 0; i < tmp.Length; i++) { tmp[i] = new Edge(tmp[i].To, tmp[i].From, tmp[i].Weight); } path = new Edge[firstpath.Length - index + tmp.Length]; for (int i = 0; i < firstpath.Length - index - 1; i++) { path[i] = new Edge(firstpath[firstpath.Length - i - 1].To, firstpath[firstpath.Length - i - 1].From, firstpath[firstpath.Length - i - 1].Weight); } path[firstpath.Length - index - 1] = edge; Array.Copy(tmp, 0, path, firstpath.Length - index, tmp.Length); return(min); }
/// <summary> /// Algorytm znajdujący drugą pod względem długości najkrótszą ścieżkę między a i b. /// Możliwe, że jej długość jest równa najkrótszej (jeśli są dwie najkrótsze ścieżki, /// algorytm zwróci jedną z nich). /// Dopuszczamy, aby na ścieżce powtarzały się wierzchołki/krawędzie. /// Można założyć, że a!=b oraz że w grafie nie występują pętle. /// </summary> /// <remarks> /// Wymagana złożoność do O(D), gdzie D jest złożonością implementacji alogorytmu Dijkstry w bibliotece Graph. /// </remarks> /// <param name="g"></param> /// <param name="path">null jeśli druga ścieżka nie istnieje, wpp ściezka jako ciąg krawędzi</param> /// <returns>null jeśli druga ścieżka nie istnieje, wpp długość znalezionej ścieżki</returns> public static double?FindSecondShortestPath(this Graph g, int a, int b, out Edge[] path) { PathsInfo[] d, dC; g.DijkstraShortestPaths(a, out d); Graph gC; if (g.Directed) { gC = g.Clone().Reverse(); } else { gC = g; } gC.DijkstraShortestPaths(b, out dC); Edge[] shortestPath = PathsInfo.ConstructPath(a, b, d); if (null == shortestPath) { path = null; return(null); } double minDist = double.MaxValue; int c = -1, c2 = -1; Edge tE = new Edge(); bool foundSame = false; foreach (Edge e in shortestPath) { if (foundSame) { break; } foreach (Edge e2 in g.OutEdges(e.From)) { if (e2.To == e.To) { continue; } double currMin = dC[e2.To].Dist + d[e.From].Dist + e2.Weight; if (minDist > currMin) { tE = e2; c = e.From; c2 = e2.To; minDist = currMin; } if (currMin == d[b].Dist) { break; } } } if (c2 == -1) { path = null; return(null); } gC.DijkstraShortestPaths(c2, out dC); Edge[] shortestPath1 = PathsInfo.ConstructPath(a, c, d); Edge[] shortestPath3 = PathsInfo.ConstructPath(c2, b, dC); List <Edge> z = new List <Edge>(); if (g.Directed) { gC.DijkstraShortestPaths(b, out dC); shortestPath3 = PathsInfo.ConstructPath(b, c2, dC); } foreach (Edge e in shortestPath1) { z.Add(e); } z.Add(tE); if (shortestPath3 == null) { path = null; return(null); } List <Edge> z2 = new List <Edge>(); foreach (Edge e in shortestPath3) { if (!g.Directed) { z.Add(e); } z2.Add(e); } if (g.Directed) { z2.Reverse(); foreach (Edge e in z2) { z.Add(new Edge(e.To, e.From, e.Weight)); } } path = z.ToArray(); // shortestPath1 + tE + shortestPath3 return(minDist); }
/// <summary> /// Wersja III i IV zadania /// Zwraca najkrótszy możliwy czas przejścia przez labirynt z użyciem co najwyżej k lasek dynamitu /// </summary> /// <param name="maze">labirynt</param> /// <param name="k">liczba dostępnych lasek dynamitu, dla wersji III k=1</param> /// <param name="path">zwracana ścieżka</param> /// <param name="t">czas zburzenia ściany</param> public int FindShortestPathWithKDynamites(char[,] maze, int k, out string path, int t) { int x = maze.GetLength(1); int y = maze.GetLength(0); int layerOffset = x * y; int vNo = 0; int start = 0; int end = 0; Graph g = new AdjacencyListsGraph <AVLAdjacencyList>(true, (k + 1) * x * y); for (int p = 0; p <= k; p++) { for (int i = 0; i < y; i++) { for (int j = 0; j < x; j++) { if (maze[i, j] == 'S' && p == 0) { start = vNo; } if (maze[i, j] == 'E') { end = vNo; } char up = i >= 1 ? maze[i - 1, j] : 'N'; char down = i < y - 1 ? maze[i + 1, j] : 'N'; char left = j >= 1 ? maze[i, j - 1] : 'N'; char right = j < x - 1 ? maze[i, j + 1] : 'N'; int nextLayerVert = vNo + layerOffset; switch (up) { case 'X': if (p < k) { g.AddEdge(vNo, nextLayerVert - x, t); } break; case 'N': break; default: g.AddEdge(vNo, vNo - x); break; } switch (down) { case 'X': if (p < k) { g.AddEdge(vNo, nextLayerVert + x, t); } break; case 'N': break; default: g.AddEdge(vNo, vNo + x); break; } switch (left) { case 'X': if (p < k) { g.AddEdge(vNo, nextLayerVert - 1, t); } break; case 'N': break; default: g.AddEdge(vNo, vNo - 1); break; } switch (right) { case 'X': if (p < k) { g.AddEdge(vNo, nextLayerVert + 1, t); } break; case 'N': break; default: g.AddEdge(vNo, vNo + 1); break; } vNo++; } } } path = ""; PathsInfo[] paths; if (!g.DijkstraShortestPaths(start, out paths)) { return(-1); } int res = -1; int minEnd = -1; int minEndValue = int.MaxValue; while (end >= 0) { if (paths[end].Last != null) { res = (int)paths[end].Dist; if (res < minEndValue) { minEnd = end; minEndValue = res; } } end -= layerOffset; } if (minEnd < 0) { return(-1); } //GET THE PATH Edge[] edgePath = PathsInfo.ConstructPath(start, minEnd, paths); StringBuilder str = new StringBuilder(); for (int i = 0; i < edgePath.Length; i++) { int layerFrom = edgePath[i].From / layerOffset; int layerTo = edgePath[i].To / layerOffset; int normalvNoFrom = edgePath[i].From - layerFrom * layerOffset; int normalvNoTo = edgePath[i].To - layerTo * layerOffset; int curXFrom = normalvNoFrom / x; int curYFrom = normalvNoFrom - x * curXFrom; int curXTo = normalvNoTo / x; int curYTo = normalvNoTo - x * curXTo; if (curXFrom == curXTo) { if (curYFrom < curYTo) { str.Append('E'); } else { str.Append('W'); } } else { if (curXFrom < curXTo) { str.Append('S'); } else { str.Append('N'); } } } path = str.ToString(); return(minEndValue); }