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