Railroad Construction: Added initial boilerplate
This commit is contained in:
90
Railroad-Construction/README.md
Normal file
90
Railroad-Construction/README.md
Normal file
@@ -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.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>Sample Input</td>
|
||||
<td>Sample Output</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><pre>
|
||||
3
|
||||
0 1.5
|
||||
1 0
|
||||
0 0</pre></td>
|
||||
<td><pre>$2.5M</pre></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><pre>
|
||||
8
|
||||
0 0
|
||||
1 1
|
||||
0 1
|
||||
3 3
|
||||
4 5
|
||||
2 2
|
||||
1 0
|
||||
3 2</pre></td>
|
||||
<td><pre>$8.7M</pre></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## 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
|
||||
```
|
||||
71
Railroad-Construction/connected_components.py
Normal file
71
Railroad-Construction/connected_components.py
Normal file
@@ -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}")
|
||||
68
Railroad-Construction/cs412_railroad_a.py
Normal file
68
Railroad-Construction/cs412_railroad_a.py
Normal file
@@ -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()
|
||||
4
Railroad-Construction/inputs/sample1.txt
Normal file
4
Railroad-Construction/inputs/sample1.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
3
|
||||
0 1.5
|
||||
1 0
|
||||
0 0
|
||||
9
Railroad-Construction/inputs/sample2.txt
Normal file
9
Railroad-Construction/inputs/sample2.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
8
|
||||
0 0
|
||||
1 1
|
||||
0 1
|
||||
3 3
|
||||
4 5
|
||||
2 2
|
||||
1 0
|
||||
3 2
|
||||
Reference in New Issue
Block a user