public void AddsEdges() { var graph = new WeightedSimpleGraph <int>(5); Assert.AreEqual(0, graph.Vertices[0].Degree); Assert.AreEqual(0, graph.Vertices[1].Degree); graph.AddEdge(0, 1, 99); Assert.AreEqual(1, graph.Vertices[0].Degree); Assert.AreEqual(1, graph.Vertices[1].Degree); Assert.AreEqual(1, graph.Vertices[0].Neighbors.Single().ID); Assert.AreEqual(0, graph.Vertices[1].Neighbors.Single().ID); Assert.AreEqual(99, graph.Vertices[0].Neighbors.Single().GetEdgeWeight(0)); Assert.AreEqual(99, graph.Vertices[1].Neighbors.Single().GetEdgeWeight(1)); graph.AddEdge(1, 4, 100); Assert.AreEqual(1, graph.Vertices[0].Degree); Assert.AreEqual(2, graph.Vertices[1].Degree); Assert.AreEqual(1, graph.Vertices[4].Degree); Assert.AreEqual(1, graph.Vertices[0].Neighbors.Single().ID); Assert.AreEqual(99, graph.Vertices[0].Neighbors.Single().GetEdgeWeight(0)); CollectionAssert.AreEquivalent(new[] { 0, 4 }, graph.Vertices[1].Neighbors.Select(n => n.ID).ToArray()); CollectionAssert.AreEquivalent(new[] { 99, 100 }, graph.Vertices[1].Neighbors.Select(n => n.GetEdgeWeight(1)).ToArray()); Assert.AreEqual(1, graph.Vertices[4].Neighbors.Single().ID); Assert.AreEqual(100, graph.Vertices[4].Neighbors.Single().GetEdgeWeight(4)); }
// This uses Prim's algorithm: "https://en.wikipedia.org/wiki/Prim's_algorithm. We don't actually need // to build the MST, just need the total cost of the streets that compose it. The heap itself can be // used to keep track of which vertices are in the MST. Arbitrarily choose the building/vertex with ID 0 // to begin creating the MST from. Using int.MaxValue as a sentinel value to represent the street cost // for a building into the growing MST when that building actually has no street into the MST. Hopefully // that doesn't lead to problems, otherwise would need nullable ints w/ a custom comparer, or something. public static long Solve(int buildingCount, int[,] streets) { var buildingGraph = WeightedSimpleGraph.CreateFromOneBasedEdges(buildingCount, streets); var connectionCosts = new BinaryHeap(buildingGraph, buildingGraph.Vertices[0]); long totalStreetCost = 0; while (!connectionCosts.IsEmpty) { var cheapestConnection = connectionCosts.Extract(); var building = cheapestConnection.Key; int costToConnectBuilding = cheapestConnection.Value; totalStreetCost += costToConnectBuilding; foreach (var neighbor in building.Neighbors) { int costToConnectNeighborFromBuilding = building.GetEdgeWeight(neighbor); int currentCostToConnectNeighbor; // The neighboring building may still be in the heap, or it might already be in the MST. If // it's still in the heap, check to see if we can improve its cost to get into the MST by // utilizing its street to the building just added. if (connectionCosts.TryGetValue(neighbor, out currentCostToConnectNeighbor)) { if (costToConnectNeighborFromBuilding < currentCostToConnectNeighbor) { connectionCosts.Update(neighbor, costToConnectNeighborFromBuilding); } } } } return(totalStreetCost); }
private static void Main() { int remainingTestCases = FastIO.ReadNonNegativeInt(); while (remainingTestCases-- > 0) { int cityCount = FastIO.ReadNonNegativeInt(); int highwayCount = FastIO.ReadNonNegativeInt(); int startCityIndex = FastIO.ReadNonNegativeInt() - 1; int endCityIndex = FastIO.ReadNonNegativeInt() - 1; var cityGraph = new WeightedSimpleGraph(cityCount); for (int h = 0; h < highwayCount; ++h) { cityGraph.AddEdge( firstVertexID: FastIO.ReadNonNegativeInt() - 1, secondVertexID: FastIO.ReadNonNegativeInt() - 1, weight: FastIO.ReadNonNegativeInt()); } int?totalMinutes = HIGHWAYS.Solve(cityGraph, startCity: cityGraph.Vertices[startCityIndex], endCity: cityGraph.Vertices[endCityIndex]); Console.WriteLine(totalMinutes?.ToString() ?? "NONE"); } }
public void ValidatesAGraph2() { // This graph is two lines and a point. var graph = WeightedSimpleGraph <int> .CreateFromOneBasedEdges(5, new[, ] { { 1, 2, 10 }, { 3, 4, 11 } }); Assert.AreEqual(1, graph.Vertices[0].Degree); Assert.AreEqual(1, graph.Vertices[1].Degree); Assert.AreEqual(1, graph.Vertices[2].Degree); Assert.AreEqual(1, graph.Vertices[3].Degree); Assert.AreEqual(0, graph.Vertices[4].Degree); Assert.IsTrue(graph.HasEdge(0, 1)); Assert.IsTrue(graph.HasEdge(2, 3)); Assert.IsFalse(graph.HasEdge(2, 0)); Assert.IsFalse(graph.HasEdge(0, 3)); Assert.IsFalse(graph.HasEdge(1, 2)); Assert.IsFalse(graph.HasEdge(3, 1)); for (int i = 0; i <= 4; ++i) { Assert.IsFalse(graph.HasEdge(4, i)); } Assert.IsFalse(graph.Vertices[0].HasNeighbor(0)); Assert.IsTrue(graph.Vertices[0].HasNeighbor(1)); Assert.IsFalse(graph.Vertices[0].HasNeighbor(2)); Assert.IsFalse(graph.Vertices[0].HasNeighbor(3)); Assert.IsFalse(graph.Vertices[0].HasNeighbor(4)); Assert.IsTrue(graph.Vertices[1].HasNeighbor(0)); Assert.IsFalse(graph.Vertices[1].HasNeighbor(1)); Assert.IsFalse(graph.Vertices[1].HasNeighbor(2)); Assert.IsFalse(graph.Vertices[1].HasNeighbor(3)); Assert.IsFalse(graph.Vertices[1].HasNeighbor(4)); Assert.IsFalse(graph.Vertices[2].HasNeighbor(0)); Assert.IsFalse(graph.Vertices[2].HasNeighbor(1)); Assert.IsFalse(graph.Vertices[2].HasNeighbor(2)); Assert.IsTrue(graph.Vertices[2].HasNeighbor(3)); Assert.IsFalse(graph.Vertices[2].HasNeighbor(4)); Assert.IsFalse(graph.Vertices[3].HasNeighbor(0)); Assert.IsFalse(graph.Vertices[3].HasNeighbor(1)); Assert.IsTrue(graph.Vertices[3].HasNeighbor(2)); Assert.IsFalse(graph.Vertices[3].HasNeighbor(3)); Assert.IsFalse(graph.Vertices[3].HasNeighbor(4)); Assert.IsFalse(graph.Vertices[4].HasNeighbor(0)); Assert.IsFalse(graph.Vertices[4].HasNeighbor(1)); Assert.IsFalse(graph.Vertices[4].HasNeighbor(2)); Assert.IsFalse(graph.Vertices[4].HasNeighbor(3)); Assert.IsFalse(graph.Vertices[4].HasNeighbor(4)); Assert.AreEqual(10, graph.Vertices[0].GetEdgeWeight(1)); Assert.AreEqual(11, graph.Vertices[2].GetEdgeWeight(3)); }
// For example, an edge like (1, 2, 4) => there's an edge between vertices 0 and 1 with weight 4. public static WeightedSimpleGraph CreateFromOneBasedEdges(int vertexCount, int[,] edges) { var graph = new WeightedSimpleGraph(vertexCount); for (int i = 0; i < edges.GetLength(0); ++i) { graph.AddEdge(edges[i, 0] - 1, edges[i, 1] - 1, edges[i, 2]); } return(graph); }
// This uses Dijkstra's algorithm: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm. // We return immediately upon visiting the destination intersection, and we don't initialize // the heap with all intersections. We only add intersections to the heap when reaching one // of their neighbor intersections. Without a pre-filled heap to rely on, we track what // intersections have been visited using an array of bools. public static double Solve(WeightedSimpleGraph intersectionGraph) { var startIntersection = intersectionGraph.Vertices.First(); var endIntersection = intersectionGraph.Vertices.Last(); var pathSafetyProbabilities = new BinaryHeap(startIntersection, topValue: 1); bool[] visitedIntersections = new bool[intersectionGraph.VertexCount]; while (!pathSafetyProbabilities.IsEmpty) { // Instead of choosing the closest neighbor, we choose the safest (highest probability). var safestPath = pathSafetyProbabilities.Extract(); var intersection = safestPath.Key; double safetyProbabilityToIntersection = safestPath.Value; if (intersection == endIntersection) { return(safetyProbabilityToIntersection * 100); } foreach (var neighbor in intersection.Neighbors.Where(n => !visitedIntersections[n.ID])) { double safetyProbabilityToNeighborThroughIntersection = safetyProbabilityToIntersection * (intersection.GetEdgeWeight(neighbor) / (double)100); double currentSafetyProbabilityToNeighbor; // We know the neighboring intersection hasn't been visited yet, so we need to maintain // its safety probability in the heap. If it's already in the heap, see if a safer path // exists to it through the intersection we're visiting. If it isn't in the heap yet, add it. if (pathSafetyProbabilities.TryGetValue(neighbor, out currentSafetyProbabilityToNeighbor)) { if (safetyProbabilityToNeighborThroughIntersection > currentSafetyProbabilityToNeighbor) { pathSafetyProbabilities.Update(neighbor, safetyProbabilityToNeighborThroughIntersection); } } else { pathSafetyProbabilities.Add(neighbor, safetyProbabilityToNeighborThroughIntersection); } } visitedIntersections[intersection.ID] = true; } throw new NotSupportedException(); }
public BinaryHeap(WeightedSimpleGraph graph, Vertex topKey, int topValue = 0) { _keyValuePairs.Add(new KeyValuePair <Vertex, int>(topKey, topValue)); _keyIndices.Add(topKey, 0); foreach (var vertex in graph.Vertices) { if (vertex.Equals(topKey)) { continue; } _keyValuePairs.Add(new KeyValuePair <Vertex, int>(vertex, int.MaxValue)); _keyIndices.Add(vertex, _keyValuePairs.Count - 1); } }
// This uses Dijkstra's algorithm: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm. // We return immediately upon visiting the destination city, and we don't initialize the // heap with all cities. We only add cities to the heap when reaching one of their neighbor // cities. Without a pre-filled heap to rely on, we track what cities have been visited // using an array of bools. public static int?Solve(WeightedSimpleGraph cityGraph, Vertex startCity, Vertex endCity) { var pathCosts = new BinaryHeap(startCity); bool[] visitedCities = new bool[cityGraph.VertexCount]; while (!pathCosts.IsEmpty) { var cheapestPath = pathCosts.Extract(); var city = cheapestPath.Key; int pathCostToCity = cheapestPath.Value; if (city == endCity) { return(pathCostToCity); } foreach (var neighbor in city.Neighbors.Where(n => !visitedCities[n.ID])) { int pathCostToNeighborThroughCity = pathCostToCity + city.GetEdgeWeight(neighbor); int currentPathCostToNeighbor; // We know the neighboring city hasn't been visited yet, so we need to maintain its // path cost in the heap. If it's already in the heap, see if a cheaper path exists // to it through the city we're visiting. If it isn't in the heap yet, add it. if (pathCosts.TryGetValue(neighbor, out currentPathCostToNeighbor)) { if (pathCostToNeighborThroughCity < currentPathCostToNeighbor) { pathCosts.Update(neighbor, pathCostToNeighborThroughCity); } } else { pathCosts.Add(neighbor, pathCostToNeighborThroughCity); } } visitedCities[city.ID] = true; } return(null); }
public void TryGetEdgeWeight() { var graph = new WeightedSimpleGraph <int>(5); int value; bool hasValue; hasValue = graph.Vertices[0].TryGetEdgeWeight(1, out value); Assert.AreEqual(0, value); Assert.IsFalse(hasValue); graph.AddEdge(0, 3, 99); hasValue = graph.Vertices[0].TryGetEdgeWeight(3, out value); Assert.AreEqual(99, value); Assert.IsTrue(hasValue); hasValue = graph.Vertices[3].TryGetEdgeWeight(0, out value); Assert.AreEqual(99, value); Assert.IsTrue(hasValue); hasValue = graph.Vertices[0].TryGetEdgeWeight(1, out value); Assert.AreEqual(0, value); Assert.IsFalse(hasValue); }
private static void Main() { int intersectionCount; while ((intersectionCount = FastIO.ReadNonNegativeInt()) != 0) { int streetCount = FastIO.ReadNonNegativeInt(); var intersectionGraph = new WeightedSimpleGraph(intersectionCount); for (int i = 0; i < streetCount; ++i) { intersectionGraph.AddEdge( firstVertexID: FastIO.ReadNonNegativeInt() - 1, secondVertexID: FastIO.ReadNonNegativeInt() - 1, weight: FastIO.ReadNonNegativeInt()); } Console.WriteLine( $"{CHICAGO.Solve(intersectionGraph):F6} percent"); } }
public void ValidatesAGraph1() { // This graph is a triangle. var graph = WeightedSimpleGraph <int> .CreateFromZeroBasedEdges(3, new[, ] { { 0, 1, 1 }, { 0, 2, 2 }, { 1, 2, 3 } }); Assert.AreEqual(2, graph.Vertices[0].Degree); Assert.AreEqual(2, graph.Vertices[1].Degree); Assert.AreEqual(2, graph.Vertices[2].Degree); Assert.IsTrue(graph.HasEdge(0, 1)); Assert.IsTrue(graph.HasEdge(1, 0)); Assert.IsTrue(graph.HasEdge(0, 2)); Assert.IsTrue(graph.HasEdge(1, 2)); Assert.IsFalse(graph.HasEdge(1, 1)); Assert.IsFalse(graph.Vertices[0].HasNeighbor(0)); Assert.IsTrue(graph.Vertices[0].HasNeighbor(1)); Assert.IsTrue(graph.Vertices[0].HasNeighbor(2)); Assert.IsTrue(graph.Vertices[1].HasNeighbor(0)); Assert.IsFalse(graph.Vertices[1].HasNeighbor(1)); Assert.IsTrue(graph.Vertices[1].HasNeighbor(2)); Assert.IsTrue(graph.Vertices[2].HasNeighbor(0)); Assert.IsTrue(graph.Vertices[2].HasNeighbor(1)); Assert.IsFalse(graph.Vertices[2].HasNeighbor(2)); Assert.AreEqual(1, graph.Vertices[0].GetEdgeWeight(1)); Assert.AreEqual(2, graph.Vertices[0].GetEdgeWeight(2)); Assert.AreEqual(1, graph.Vertices[1].GetEdgeWeight(0)); Assert.AreEqual(3, graph.Vertices[1].GetEdgeWeight(2)); Assert.AreEqual(2, graph.Vertices[2].GetEdgeWeight(0)); Assert.AreEqual(3, graph.Vertices[2].GetEdgeWeight(1)); }
internal Vertex(WeightedSimpleGraph graph, int ID) { _graph = graph; this.ID = ID; }