Esempio n. 1
0
        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));
        }
Esempio n. 2
0
    // 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);
    }
Esempio n. 3
0
    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");
        }
    }
Esempio n. 4
0
        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));
        }
Esempio n. 5
0
    // 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);
    }
Esempio n. 6
0
    // 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();
    }
Esempio n. 7
0
    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);
        }
    }
Esempio n. 8
0
    // 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);
    }
Esempio n. 9
0
        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);
        }
Esempio n. 10
0
    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");
        }
    }
Esempio n. 11
0
        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));
        }
Esempio n. 12
0
 internal Vertex(WeightedSimpleGraph graph, int ID)
 {
     _graph  = graph;
     this.ID = ID;
 }