diff --git a/Railroad-Construction/README.md b/Railroad-Construction/README.md new file mode 100644 index 0000000..202e626 --- /dev/null +++ b/Railroad-Construction/README.md @@ -0,0 +1,90 @@ +# Lab 12: Railroad Construction + +## Specification + +There are _n_ cities that want to create a shared train network so that each +pair of cities is connected by rail to each of the other cities. The cost of +laying track is directly proportional to the distance between two cities. The +cost to lay one mile of railroad track is $1M. Your task is to determine the +lowest cost possible for laying the track so that all the cities are connected. + +## Input + +A single line containing the number n of cities followed by n city coordinate +lines. Each of the coordinate lines is a pair of floating point numbers x y +giving the coordinates of the cities on a square grid (for this problem you may +assume the earth is flat and that the units of the grid structure are given in +miles). + +## Output + +The minimum amount of money (in millions) that must be spent to create the +railroad network. You can round this to a single decimal place. + + + + + + + + + + + + + + +
Sample InputSample Output
+3
+0 1.5
+1 0
+0 0
$2.5M
+8
+0 0
+1 1
+0 1
+3 3
+4 5
+2 2
+1 0
+3 2
$8.7M
+ +## Hints + +1. You may use our [connected_components.py](./connected_components.py) Download + connected_components.py in your code if you want., which will produce labels + for each of the vertices that correspond to their component. You will need to + use the adjacency list structure as suggested below if you wish to use this + code in its unmodified state. +2. You may have already envisioned a way to represent this problem as a graph. + Draw the graph in the small example above on paper. How many edges does it + have? If you are unsure of this, check with me before you start coding the + solution. + +## Adjacency List + +Augment your dictionary/set based adjacency structure to use a dictionary for +the edges (instead of a set as before). This will allow you to store the edge +weights as the values of this second dictionary. + +## Method/Programming requirement + +Your program must implement either Borůvka's algorithm (I suggest you use +Borůvka's) or Prims (not Kruskal). You must specify which algorithm you are +implementing in the comments. Any other method to compute the answer will result +in zero credit. + +## Submission + +Submit the code in a file named [cs412_railroad_a.py](./cs412_railroad_a.py) to +Gradescope. If you use the supplied copy of +[connected_components.py](./connected_components.py), you only need to import it +into your code (it will be available on Gradescope, so, no need to submit). If +you change connected_components.py, then please rename it and submit it as part +of your code (or you can copy/paste the code into your lab and just submit a +single file, it is up to you). You can import the code using the following +python: + +``` +from connected_components import count_and_label +``` diff --git a/Railroad-Construction/connected_components.py b/Railroad-Construction/connected_components.py new file mode 100644 index 0000000..1884d1f --- /dev/null +++ b/Railroad-Construction/connected_components.py @@ -0,0 +1,71 @@ +# Implementation of the count-and-label +# connected component counting algorithm. +# +# Author: John Bowers +# Version: Mar 17, 2021 + +# March 2022 -- molloykp +# Changed code to label components starting a 0 (instead of 1) +# October 2023 -- molloykp +# Change code to accept new adj list/hash map structure + +# Version of the iterative dfs that takes as input +# a list of labels, one per vertex, and a starting +# vertex v and uses iterative depth-first-search +# to label each vertex with a given currentLabel +# +# Parameters: +# * graph is an adjaceny list where vertices name/keys are stored +# in a dictionary. Each vertex has its own dictionary +# where the key is the edge endpoint and the value is the +# weight of the edge. +# * v is the vertex to DFS from +# * labels is an array of length V which is -1 if the vertex is unvisited +# * currentLabel is the label to set on every visited vertex +# +# labels is an out-parameter and will be modified destructively during the +# run of this operation. +# + +def dfs_label(graph, v, labels, currentLabel): + bag = [v] + while bag: # while bag is not empty + u = bag.pop() + if labels[u] == -1: + labels[u] = currentLabel + for w in graph[u]: + bag.append(w) + +# Counts the number of connected components in the graph +# and labels each vertex with its connected component index +# +# Parameters: +# * graph given as an adjaceny list/set structure with vertices 0..(n-1) +# +# Returns: +# count, labels +# where count is the number of connected components in the graph +# and labels is the labeling of each vertex's connected component +# starting from index 0 +def count_and_label(graph): + labels = [-1 for _ in range(len(graph))] # Initially all labels are -1 + count = -1 + for v in range(len(graph)): # for each vertex + if labels[v] == -1: # if v is not visited + count += 1 + dfs_label(graph, v, labels, count) + return count+1, labels + +if __name__ == "__main__": + graph = { + 0: {1:2}, # 0's neighbors + 1: {0:2, 2:4, 3:1}, # 1's neighbors + 2: {1:4, 3:5}, # 2's neighbors + 3: {1:1, 2:5}, # 3's neighbors + 4: {5:8}, # 4's neighbors + 5: {4:8} # 5's neighbors + } + + count, labels = count_and_label(graph) + print(f"Number of connected components: {count}") + print(f"Vertex labels: {labels}") diff --git a/Railroad-Construction/cs412_railroad_a.py b/Railroad-Construction/cs412_railroad_a.py new file mode 100644 index 0000000..672e1d2 --- /dev/null +++ b/Railroad-Construction/cs412_railroad_a.py @@ -0,0 +1,68 @@ +""" +name: Nicholas Tamassia + +Honor Code and Acknowledgments: + + This work complies with the JMU Honor Code. + + Comments here on your code and submission. +""" + +import pprint +from collections import defaultdict +from math import sqrt + +type Coordinate = tuple[float, float] +type CoordMap = dict[int, Coordinate] +type WeightedGraph = dict[int, dict[int, float]] + + +def distance(a: Coordinate, b: Coordinate) -> float: + x1, y1 = a + x2, y2 = b + return sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2) + + +def create_graph(coords: CoordMap) -> WeightedGraph: + graph: WeightedGraph = defaultdict(dict) + + coord_items = coords.items() + + for label1, coord1 in coord_items: + for label2, coord2 in coord_items: + if label1 == label2: + continue + + weight = distance(coord1, coord2) + graph[label1][label2] = weight + graph[label2][label1] = weight + + return graph + + +def calculate_cost(graph: WeightedGraph) -> float: + cost: float = 0.0 + + return cost + + +# All modules for CS 412 must include a main method that allows it +# to imported and invoked from other python scripts +def main(): + n: int = int(input()) + + coords: CoordMap = {} + + for i in range(0, n): + tokens = input().split() + coords[i] = (float(tokens[0]), float(tokens[1])) + + weighted_graph = create_graph(coords) + + pprint.pprint(weighted_graph) + + # print(f"${cost:.1f}M") + + +if __name__ == "__main__": + main() diff --git a/Railroad-Construction/inputs/sample1.txt b/Railroad-Construction/inputs/sample1.txt new file mode 100644 index 0000000..f6d5b22 --- /dev/null +++ b/Railroad-Construction/inputs/sample1.txt @@ -0,0 +1,4 @@ +3 +0 1.5 +1 0 +0 0 diff --git a/Railroad-Construction/inputs/sample2.txt b/Railroad-Construction/inputs/sample2.txt new file mode 100644 index 0000000..0a0b1af --- /dev/null +++ b/Railroad-Construction/inputs/sample2.txt @@ -0,0 +1,9 @@ +8 +0 0 +1 1 +0 1 +3 3 +4 5 +2 2 +1 0 +3 2