Compare commits
26 Commits
d1dead52e1
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 0dab59e81e | |||
| d3e7ce3189 | |||
| c6c7e667c3 | |||
| 7739e18c7f | |||
| 38117921ed | |||
| 97e92b8b14 | |||
| a730e01e9c | |||
| 82ba7de3eb | |||
| e1b3928cf4 | |||
| 1ecef01de4 | |||
| ff22086802 | |||
| 6a9630c5ac | |||
| ac34fbcb83 | |||
| 555e084620 | |||
| 842ea1d43a | |||
| aaa1fed779 | |||
| 31604a4b6d | |||
| 16acd96a27 | |||
| 67d08e827d | |||
| 040e551203 | |||
| e3f82b9c3c | |||
| 5e7253c3be | |||
| f51d122a33 | |||
| 5aba700f48 | |||
| 070d6dbe3e | |||
| 4ddf200373 |
79
Arbitrages/README.md
Normal file
79
Arbitrages/README.md
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
# Homework 6: Arbitrages
|
||||||
|
|
||||||
|
## Finding an Arbitrage
|
||||||
|
|
||||||
|
In currency exchange markets units of one currency, such as US Dollars, can be exchanged for units of a different currency, like Italian Lira. These exchanges are set by the market's exchange rate, which typically fluctuates throughout the day. For example, at the time of this assignment's writing, the exchange rate between the US Dollar (USD) and the Great British Pound (GBP) was 1 USD to 0.77 GBP. This means that $150 is worth $150\cdot0.77=115.5\ \text{GBP}$ and $150\ \text{GPB} = 150 / 0.77 = \$194.81$. The exchange rates are dynamic and constantly change.
|
||||||
|
|
||||||
|
This change in currency is based on how trades are progressing through the market, and the changing dynamic nature of the exchange leads to a particular inefficiency in short-time frame based trading. For example, consider the following exchange rates:
|
||||||
|
|
||||||
|
- 1 USD = 0.82 Euro
|
||||||
|
- 1 Euro = 129.7 Japanese Yen
|
||||||
|
- 1 Japanese Yen = 12 Turkish Lira
|
||||||
|
- 1 Turkish Lira = 0.0008 USD.
|
||||||
|
|
||||||
|
So if we start with 1 USD, and make all of the exchanges above, we get $1\cdot0.82\cdot129.7\cdot12\cdot.0008 = \$1.021$. In other words, by making four exchanges starting from $1.00 we end up with $1.02, a 2% profit. This situation, where a cycle of currency exchanges starting and ending in the same currency leads to a net gain in the starting currency (rather than breaking even) is called arbitrage.
|
||||||
|
|
||||||
|
Notice that arbitrage occurs when the product of all of the exchanges in a cycle is greater than 1. Your job is to find and detect arbitrage in a given exchange and determine what sequence of exchanges need to be made.
|
||||||
|
|
||||||
|
We are going to reduce this problem to the problem of finding negative weight cycles in a graph. There are two problems to overcome.
|
||||||
|
|
||||||
|
1. Our formulation of the problem requires a product of the exchange rates, but shortest path algorithms work with the sum of the weights of a bunch of edges.
|
||||||
|
2. Negative weight cycles are "smaller" whereas we are attempting to maximize a profit.
|
||||||
|
|
||||||
|
The first problem can be overcome using the following trick: the logarithm function is monotonic, which means that log(x) increases when x increases. Furthermore, the logarithm function can be used to convert products into sums because $\log(a\cdot{b}) = \log(a) + \log(b)$. Thus, if we want to maximize $a\cdot{b}$, this is the same as maximizing $\log(a) + \log(b)$. Also, consider that $a\cdot{b} > 1$ if and only if $\log(a) + \log(b) > 0$. Thus a cycle of exchange rates $r1\cdot{r2}\cdot{r3}\cdot...\cdot{rn} > 1$ if and only if $\log(r1) + \log(r2) + ... + \log(rn) > 0$.
|
||||||
|
|
||||||
|
But now the second problem is that we want to detect negative weight cycles because Bellman-Ford can do that for us, but above we described a positive weight cycle. Now we can use the following trick--simply negate each log value. $\log(r1) + \log(r2) + ... + \log(rn) > 0$ if and only if $(-\log(r1)) + (-\log(r2)) + ... + (-\log(rn)) < 0$. Now, you should be able to see how to build a graph, with some appropriate weights, and use it to find arbitrage cycles directly!
|
||||||
|
|
||||||
|
### Input
|
||||||
|
|
||||||
|
The first line of the input will contain a single integer m describing how many exchange rates are in the exchange. The next _m_ lines will be given by _cIn_ _cOut_ _r_ where _cIn_ is the code for the starting currency (like USD or GPB), _cOut_ is the code for the ending currency, and _r_ is the exchange rate (i.e. "USD GBP 0.75" codes for 1 USD = 0.75 GBP).
|
||||||
|
|
||||||
|
### Output and Rubrics
|
||||||
|
|
||||||
|
This assignment is worth 15 points.
|
||||||
|
|
||||||
|
- 9 points (partial) -- code identifies an the presence or absence of an arbitrage correctly on published test cases
|
||||||
|
- 3 points (partial) -- code identifies an the presence or absence of an arbitrage correctly on hidden test cases
|
||||||
|
- 2 points (full credit) -- code outputs the actual exchanges correctly and in the format specified on the public test cases
|
||||||
|
- 1 point (full credit) -- code outputs the actual exchanges correctly and in the format specified on the hidden test cases
|
||||||
|
|
||||||
|
For full credit, when an arbitrage is detected, in addition to the output above, your code should output the actual exchanges that need to be made by showing each currency code in the exchange separated by " => " on a second line, and a third line containing the actual change as a multiplicative factor with the format "X factor increase", which should be **formatted to 5 decimal points**.
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>Sample Input</td>
|
||||||
|
<td>Partial Credit Output</td>
|
||||||
|
<td>Full Credit Output</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><pre>
|
||||||
|
4
|
||||||
|
USD EUR 0.82
|
||||||
|
EUR JPY 129.7
|
||||||
|
JPY TRY 12
|
||||||
|
TRY USD 0.0008</pre></td>
|
||||||
|
<td><pre>Arbitrage Detected</pre></td>
|
||||||
|
<td><pre>
|
||||||
|
Arbitrage Detected
|
||||||
|
USD => EUR => JPY => TRY => USD
|
||||||
|
1.02100 factor increase</pre></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>Sample Input</td>
|
||||||
|
<td>Sample Output (for both partial and full credit options)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><pre>
|
||||||
|
2
|
||||||
|
USD GBP 1.0
|
||||||
|
GBP USD 1.0</pre></td>
|
||||||
|
<td><pre>No Arbitrage Detected</pre></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
### Submission
|
||||||
|
|
||||||
|
Submit this assignment as `cs412_arbitrages_a.py` to Gradescope.
|
||||||
83
Arbitrages/cs412_arbitrages_a.py
Normal file
83
Arbitrages/cs412_arbitrages_a.py
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
"""
|
||||||
|
name: Nicholas Tamassia
|
||||||
|
|
||||||
|
Honor Code and Acknowledgments:
|
||||||
|
|
||||||
|
This work complies with the JMU Honor Code.
|
||||||
|
|
||||||
|
Comments here on your code and submission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import math
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
|
||||||
|
def find_arbitrage(graph: dict[str, dict[str, float]]) -> list[str] | None:
|
||||||
|
weights: dict[str, float] = {c: math.inf for c in graph}
|
||||||
|
prev: dict[str, str | None] = {c: None for c in graph}
|
||||||
|
start: str = list(graph.keys())[0]
|
||||||
|
|
||||||
|
weights[start] = 0
|
||||||
|
for _ in range(len(graph) - 1):
|
||||||
|
for src in graph:
|
||||||
|
for dest, rate in graph[src].items():
|
||||||
|
weight: float = -math.log(rate)
|
||||||
|
if weights[src] + weight < weights[dest]:
|
||||||
|
weights[dest] = weights[src] + weight
|
||||||
|
prev[dest] = src
|
||||||
|
|
||||||
|
node: str | None = None
|
||||||
|
for src in graph:
|
||||||
|
for dest, rate in graph[src].items():
|
||||||
|
weight: float = -math.log(rate)
|
||||||
|
if weights[src] + weight < weights[dest]:
|
||||||
|
node = dest
|
||||||
|
prev[dest] = src
|
||||||
|
break
|
||||||
|
if node is not None:
|
||||||
|
break
|
||||||
|
|
||||||
|
if node is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
for _ in range(len(graph)):
|
||||||
|
node = prev[node]
|
||||||
|
|
||||||
|
path = [node]
|
||||||
|
while True:
|
||||||
|
node = prev[node]
|
||||||
|
path.append(node)
|
||||||
|
if node == path[0]:
|
||||||
|
break
|
||||||
|
|
||||||
|
path.reverse()
|
||||||
|
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
n: int = int(input().strip())
|
||||||
|
graph: dict[str, dict[str, float]] = defaultdict(dict)
|
||||||
|
|
||||||
|
for _ in range(n):
|
||||||
|
vals: list[str] = input().strip().split()
|
||||||
|
src: str = vals[0]
|
||||||
|
dest: str = vals[1]
|
||||||
|
rate: float = float(vals[2])
|
||||||
|
graph[src][dest] = rate
|
||||||
|
|
||||||
|
path = find_arbitrage(graph)
|
||||||
|
if path is not None:
|
||||||
|
print("Arbitrage Detected")
|
||||||
|
print(" => ".join(path))
|
||||||
|
|
||||||
|
factor = 1.0
|
||||||
|
for i in range(len(path) - 1):
|
||||||
|
factor *= graph[path[i]][path[i + 1]]
|
||||||
|
print(f"{factor:.5f} factor increase")
|
||||||
|
else:
|
||||||
|
print("No Arbitrage Detected")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
5
Arbitrages/inputs/sample1.txt
Normal file
5
Arbitrages/inputs/sample1.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
4
|
||||||
|
USD EUR 0.82
|
||||||
|
EUR JPY 129.7
|
||||||
|
JPY TRY 12
|
||||||
|
TRY USD 0.0008
|
||||||
3
Arbitrages/inputs/sample2.txt
Normal file
3
Arbitrages/inputs/sample2.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
2
|
||||||
|
USD GBP 1.0
|
||||||
|
GBP USD 1.0
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# Coding 3: Backtracking
|
# Homework 3: Coding Backtracking
|
||||||
|
|
||||||
## Rocket Sections
|
## Rocket Sections
|
||||||
|
|
||||||
@@ -114,3 +114,41 @@ as `cs412_hw3_a.py` to Gradescope.
|
|||||||
- (10 points) Does the code solve the samples in the write-up?
|
- (10 points) Does the code solve the samples in the write-up?
|
||||||
- (7 point) Does the code solve the private testing samples?
|
- (7 point) Does the code solve the private testing samples?
|
||||||
- (3 points) For solving the full credit option.
|
- (3 points) For solving the full credit option.
|
||||||
|
|
||||||
|
# Homework 4: Coding Dynamic Programming
|
||||||
|
|
||||||
|
Modify your solution to the backtracking version of Coding Homework 3:
|
||||||
|
Backtracking to utilize dynamic programming in an iterative way rather than
|
||||||
|
recursive. You should clearly separate the dynamic programming phase of your
|
||||||
|
algorithm from the solution building phase. As with the previous iterations of
|
||||||
|
this homework, partial credit may be obtained by solving the optimization
|
||||||
|
problem but not producing a full solution; however, this time more weight is
|
||||||
|
given to solving the full credit option.
|
||||||
|
|
||||||
|
The input/output samples are the same as for Coding Homework 3: Backtracking.
|
||||||
|
|
||||||
|
## Your Task
|
||||||
|
|
||||||
|
Develop a dynamic programming solution to the problem. Turn your solution in as
|
||||||
|
`cs412_rockets_dynamic.py`.
|
||||||
|
|
||||||
|
## Coding Requirements
|
||||||
|
|
||||||
|
No points will be awarded if any of the following techniques are in your code:
|
||||||
|
|
||||||
|
- dictionaries
|
||||||
|
- copying of lists or other objects
|
||||||
|
- not allocating the dynamic memory structure in a single (or at more 2 lines)
|
||||||
|
- Retrieving which parts were used MUST be performed by tracing back through the
|
||||||
|
dynamic/memoized structure. No points will be awarded for that portion of the
|
||||||
|
HW if this is performed differently.
|
||||||
|
|
||||||
|
Gradescope does not check for these items. Your code will be manually reviewed
|
||||||
|
after submission.
|
||||||
|
|
||||||
|
## Rubric (see notes above for coding requirements)
|
||||||
|
|
||||||
|
- (1 point) Does your code pass the pep8 coding guidelines?
|
||||||
|
- (5 points) Does the code solve the samples in the write-up?
|
||||||
|
- (4 point) Does the code solve the private testing samples?
|
||||||
|
- (1 points) instructor points
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ def construct(sections: list[int], target: int) -> dict[int, int]:
|
|||||||
# All modules for CS 412 must include a main method that allows it
|
# All modules for CS 412 must include a main method that allows it
|
||||||
# to imported and invoked from other python scripts
|
# to imported and invoked from other python scripts
|
||||||
def main():
|
def main():
|
||||||
sections: list[int] = list(map(int, input().split(" ")))
|
sections: list[int] = list(map(int, input().split()))
|
||||||
target: int = int(input())
|
target: int = int(input())
|
||||||
|
|
||||||
best_sections = construct(sections, target)
|
best_sections = construct(sections, target)
|
||||||
|
|||||||
68
Backtracking/cs412_rockets_dynamic.py
Normal file
68
Backtracking/cs412_rockets_dynamic.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.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def construct(sections: list[int], target: int) -> list[tuple[int, int]]:
|
||||||
|
s: list[int] = sorted(sections, reverse=True)
|
||||||
|
n: int = len(s)
|
||||||
|
|
||||||
|
IMPOSSIBLE: int = target + 1
|
||||||
|
|
||||||
|
memo: list[list[int]] = [([IMPOSSIBLE] * (target + 1)) for _ in range(0, n + 1)]
|
||||||
|
|
||||||
|
for i in range(n + 1):
|
||||||
|
memo[i][0] = 0
|
||||||
|
|
||||||
|
for i in range(n - 1, -1, -1):
|
||||||
|
for t in range(1, target + 1):
|
||||||
|
skip: int = memo[i + 1][t]
|
||||||
|
|
||||||
|
t_new: int = t - s[i]
|
||||||
|
use: int = memo[i][t_new] + 1 if t_new >= 0 else IMPOSSIBLE
|
||||||
|
|
||||||
|
memo[i][t] = min(skip, use)
|
||||||
|
|
||||||
|
total_sections: list[int] = [0] * n
|
||||||
|
|
||||||
|
i, t = 0, target
|
||||||
|
|
||||||
|
while i < n or t > 0:
|
||||||
|
skipped: int = memo[i + 1][t]
|
||||||
|
|
||||||
|
t_new2: int = t - s[i]
|
||||||
|
used: int = memo[i][t_new2] + 1 if t_new2 >= 0 else IMPOSSIBLE
|
||||||
|
|
||||||
|
if used <= skipped:
|
||||||
|
total_sections[i] += 1
|
||||||
|
t -= s[i]
|
||||||
|
else:
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
return list(zip(s, total_sections))
|
||||||
|
|
||||||
|
|
||||||
|
# All modules for CS 412 must include a main method that allows it
|
||||||
|
# to imported and invoked from other python scripts
|
||||||
|
def main():
|
||||||
|
sections: list[int] = list(map(int, input().split()))
|
||||||
|
target: int = int(input())
|
||||||
|
|
||||||
|
best_sections: list[tuple[int, int]] = construct(sections, target)
|
||||||
|
total: int = 0
|
||||||
|
|
||||||
|
for i, n in sorted(best_sections):
|
||||||
|
total += n
|
||||||
|
print(f"{n} of length {i}")
|
||||||
|
|
||||||
|
print(f"{total} rocket sections minimum")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
104
Basic-Graphs/README.md
Normal file
104
Basic-Graphs/README.md
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
# Homework 5: Basic Graphs
|
||||||
|
|
||||||
|
## Preface
|
||||||
|
|
||||||
|
This problem comes directly from a CS 412 student's coding interview with
|
||||||
|
Amazon. The student was given one hour to solve three different problems and
|
||||||
|
this was one of the problems. The easiest way to solve this problem quickly with
|
||||||
|
a minimal amount of code is recursively, which is probably the right way to
|
||||||
|
knock something like this out under coding interview pressure. This is also a
|
||||||
|
very slight twist on a very standard coding interview question.
|
||||||
|
|
||||||
|
You only need to code up one implementation and turn it in and I don't care if
|
||||||
|
it's the iterative or the recursive solution; however, I suggest you take the
|
||||||
|
following approach to this problem:
|
||||||
|
|
||||||
|
1. Set aside one hour to approach this problem fresh and attempt to knock out a
|
||||||
|
complete solution to the problem on your first try in under an hour. You
|
||||||
|
haven't been practicing this sort of thing (that is, trying to code under a
|
||||||
|
strict time limit), so its likely that you won't succeed within the hour time
|
||||||
|
limit. The purpose of this exercise is for you go gauge where you are at.
|
||||||
|
2. Take your time in coding a solution. I suggest you code both a recursive
|
||||||
|
solution and an iterative solution. These are the sorts of things you should
|
||||||
|
be working to get comfortable with coding as second nature. Obviously, the
|
||||||
|
first time you code it, it will not be second nature yet. You have to do it a
|
||||||
|
lot before it becomes second nature. Practice, as always, makes perfect here.
|
||||||
|
As I always tell students: learning to code is much more like learning a
|
||||||
|
musical instrument than learning a body of facts. You only get better at what
|
||||||
|
you practice. Class time / instruction only exists to point you towards what
|
||||||
|
you need to do, but you have to go and do it.
|
||||||
|
3. Once you've coded solutions to the problem, on a different day, set aside
|
||||||
|
another hour for yourself and try to code a solution as quickly as possible
|
||||||
|
without referring to your previous solutions. You've now done the hard work
|
||||||
|
in step 2 of actually solving, coding, and debugging your first solution.
|
||||||
|
|
||||||
|
It's time to reinforce those neural pathways in your mind and make this sort of
|
||||||
|
problem a part of your repertoire--every professional cellist in the world can
|
||||||
|
play the prelude to Bach's cello suite in G without preparation (if you don't
|
||||||
|
know the piece, switch to Spotify, load up one of Yo-Yo Ma's recordings of it,
|
||||||
|
then come back here), every computer scientist should be able to bang out a
|
||||||
|
recursive solution to this problem without even thinking about it. But that only
|
||||||
|
comes with practice. I've coded variations of this probably hundreds of times at
|
||||||
|
this point, if not thousands, which is why I can live code it in class with you
|
||||||
|
without having to debug. Y'all are at the point where you have all the knowledge
|
||||||
|
you need and solidifying it practice is the final piece.
|
||||||
|
|
||||||
|
So, with Yo-Yo Ma playing Bach's Prelude to the Cello Suite in G in the
|
||||||
|
background try to rewrite your solution from scratch as quickly as possible.
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
|
||||||
|
You have been hired by an elite treasure hunting team to help them find a
|
||||||
|
treasure that was hidden on a large island in the South Pacific. The team has
|
||||||
|
obtained LIDAR scans of a large square patch of ocean and has preprocessed the
|
||||||
|
scans into an n x n array where each array slot is either a 0 or a 1. A 0
|
||||||
|
denotes ocean while a 1 denotes land. Each array slot corresponds to 1 acre of
|
||||||
|
scan and adjacent slots in the array (in the vertical or horizontal directions,
|
||||||
|
but not diagonal) that both contain a 1 are considered part of the same land
|
||||||
|
mass / island. Your task is to find the size of the largest island in acres.
|
||||||
|
|
||||||
|
## Input
|
||||||
|
|
||||||
|
The input is given by a line containing a single number _n_ followed by n lines,
|
||||||
|
each of which has n values of either 0 or 1 separated by spaces representing the
|
||||||
|
scan of the region.
|
||||||
|
|
||||||
|
## Output
|
||||||
|
|
||||||
|
The output is a single number which is the size of the largest island in the map
|
||||||
|
in acres.
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>Sample Input</td>
|
||||||
|
<td>Sample Output</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><pre>
|
||||||
|
6
|
||||||
|
0 1 0 0 0 0
|
||||||
|
0 1 1 0 0 0
|
||||||
|
0 0 0 1 1 1
|
||||||
|
0 0 1 1 1 0
|
||||||
|
0 0 1 1 0 0
|
||||||
|
1 0 0 0 0 0</pre></td>
|
||||||
|
<td><pre>8</pre></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><pre>
|
||||||
|
8
|
||||||
|
0 1 0 0 0 0 1 1
|
||||||
|
0 1 1 0 0 1 1 0
|
||||||
|
0 0 0 1 1 1 1 1
|
||||||
|
0 0 1 1 1 0 1 1
|
||||||
|
0 0 1 1 0 1 1 0
|
||||||
|
1 0 0 0 0 0 1 1
|
||||||
|
0 0 0 0 0 1 1 0
|
||||||
|
0 0 0 0 0 0 1 1</pre></td>
|
||||||
|
<td><pre>24</pre></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
## Turning it in
|
||||||
|
|
||||||
|
Name your program `cs412_islands_a.py` and turn it in on Gradescope.
|
||||||
67
Basic-Graphs/cs412_islands_a.py
Normal file
67
Basic-Graphs/cs412_islands_a.py
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
"""
|
||||||
|
name: Nicholas Tamassia
|
||||||
|
|
||||||
|
Honor Code and Acknowledgments:
|
||||||
|
|
||||||
|
This work complies with the JMU Honor Code.
|
||||||
|
|
||||||
|
Comments here on your code and submission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def explore_island(grid: list[list[int]], start: tuple[int, int]) -> int:
|
||||||
|
num_rows: int = len(grid)
|
||||||
|
num_cols: int = len(grid[0])
|
||||||
|
size: int = 0
|
||||||
|
|
||||||
|
def recurse(coord: tuple[int, int]):
|
||||||
|
x, y = coord
|
||||||
|
|
||||||
|
if grid[y][x] == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
nonlocal size
|
||||||
|
size += 1
|
||||||
|
grid[y][x] = 0
|
||||||
|
|
||||||
|
if y > 0:
|
||||||
|
recurse((x, y - 1))
|
||||||
|
if y < num_rows - 1:
|
||||||
|
recurse((x, y + 1))
|
||||||
|
if x > 0:
|
||||||
|
recurse((x - 1, y))
|
||||||
|
if x < num_cols - 1:
|
||||||
|
recurse((x + 1, y))
|
||||||
|
|
||||||
|
recurse(start)
|
||||||
|
|
||||||
|
return size
|
||||||
|
|
||||||
|
|
||||||
|
# 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())
|
||||||
|
|
||||||
|
land_grid: list[list[int]] = [list(map(int, input().split())) for _ in range(0, n)]
|
||||||
|
|
||||||
|
num_rows: int = n
|
||||||
|
num_cols: int = len(land_grid[0])
|
||||||
|
|
||||||
|
largest_island: int = -1
|
||||||
|
|
||||||
|
for y in range(0, num_rows):
|
||||||
|
for x in range(0, num_cols):
|
||||||
|
if land_grid[y][x] == 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
island_size: int = explore_island(land_grid, (x, y))
|
||||||
|
|
||||||
|
if island_size > largest_island:
|
||||||
|
largest_island = island_size
|
||||||
|
|
||||||
|
print(largest_island)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
7
Basic-Graphs/inputs/sample1.txt
Normal file
7
Basic-Graphs/inputs/sample1.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
6
|
||||||
|
0 1 0 0 0 0
|
||||||
|
0 1 1 0 0 0
|
||||||
|
0 0 0 1 1 1
|
||||||
|
0 0 1 1 1 0
|
||||||
|
0 0 1 1 0 0
|
||||||
|
1 0 0 0 0 0
|
||||||
9
Basic-Graphs/inputs/sample2.txt
Normal file
9
Basic-Graphs/inputs/sample2.txt
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
8
|
||||||
|
0 1 0 0 0 0 1 1
|
||||||
|
0 1 1 0 0 1 1 0
|
||||||
|
0 0 0 1 1 1 1 1
|
||||||
|
0 0 1 1 1 0 1 1
|
||||||
|
0 0 1 1 0 1 1 0
|
||||||
|
1 0 0 0 0 0 1 1
|
||||||
|
0 0 0 0 0 1 1 0
|
||||||
|
0 0 0 0 0 0 1 1
|
||||||
9
Basic-Graphs/inputs/sample3.txt
Normal file
9
Basic-Graphs/inputs/sample3.txt
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
8
|
||||||
|
0 0 1 0 0 0 0 1 0 0 0 0 0
|
||||||
|
0 0 0 0 0 0 0 1 1 1 0 0 0
|
||||||
|
0 1 1 0 1 0 0 0 0 0 0 0 0
|
||||||
|
0 1 0 0 1 1 0 0 1 0 1 0 0
|
||||||
|
0 1 0 0 1 1 0 0 1 1 1 0 0
|
||||||
|
0 0 0 0 0 0 0 0 0 0 1 0 0
|
||||||
|
0 0 0 0 0 0 0 1 1 1 0 0 0
|
||||||
|
0 0 0 0 0 0 0 1 1 0 0 0 0
|
||||||
@@ -11,7 +11,7 @@ import sys
|
|||||||
# All modules for CS 412 must include a main method that allows it
|
# All modules for CS 412 must include a main method that allows it
|
||||||
# to imported and invoked from other python scripts
|
# to imported and invoked from other python scripts
|
||||||
def main():
|
def main():
|
||||||
sounds: list[str] = sys.stdin.readline().strip().split(" ")
|
sounds: list[str] = sys.stdin.readline().strip().split()
|
||||||
num_animals: int = int(sys.stdin.readline().strip())
|
num_animals: int = int(sys.stdin.readline().strip())
|
||||||
|
|
||||||
animal_map: dict[str, str] = {}
|
animal_map: dict[str, str] = {}
|
||||||
|
|||||||
@@ -12,18 +12,17 @@ import sys
|
|||||||
# to imported and invoked from other python scripts
|
# to imported and invoked from other python scripts
|
||||||
def main():
|
def main():
|
||||||
|
|
||||||
sounds: list[str] = sys.stdin.readline().strip().split(" ")
|
sounds: list[str] = sys.stdin.readline().strip().split()
|
||||||
num_animals: int = int(sys.stdin.readline().strip())
|
num_animals: int = int(sys.stdin.readline().strip())
|
||||||
|
|
||||||
fox_sounds: list[str] = []
|
fox_sounds: list[str] = []
|
||||||
animals_ecountered: list[str] = []
|
animals_ecountered: list[str] = []
|
||||||
|
|
||||||
animal_sounds: list[tuple[str, str]] = list(
|
animal_sounds: list[tuple[str, str]] = []
|
||||||
map(
|
|
||||||
lambda line: line.strip().split(" goes "),
|
for line in sys.stdin.readlines()[:num_animals]:
|
||||||
sys.stdin.readlines()[:num_animals],
|
split_line = line.strip().split(" goes ")
|
||||||
)
|
animal_sounds.append((split_line[0], split_line[1]))
|
||||||
)
|
|
||||||
|
|
||||||
for sound in sounds:
|
for sound in sounds:
|
||||||
animal_found = False
|
animal_found = False
|
||||||
|
|||||||
86
Graph-Search/README.md
Normal file
86
Graph-Search/README.md
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
# Lab 11: Graph Search - Bus Stops
|
||||||
|
|
||||||
|
## Graph Search Basics
|
||||||
|
|
||||||
|
The purpose of this lab is to give you practice implementing graph data
|
||||||
|
structures and basic graph traversal and reachability queries.
|
||||||
|
|
||||||
|
You are building an app for a newly created bus system connecting Harrisonburg
|
||||||
|
to Charlottesville. There are many different bus routes each of which travels
|
||||||
|
between named stops along its route. Not all of the bus routes cross each other,
|
||||||
|
so it is possible that there are some pairs of stops where there is no way of
|
||||||
|
getting on a bus at the first stop and making changes to get to the final stop.
|
||||||
|
Your task is to implement a graph search function that can determine a sequence
|
||||||
|
of stops a user can use to get from a starting stop to an ending stop, or to
|
||||||
|
tell the user that there is no bus route possible between those two stops.
|
||||||
|
|
||||||
|
## Input
|
||||||
|
|
||||||
|
The bus route segments are described as single stretches of road between two
|
||||||
|
consecutive stops. All such pairs will be described in one direction, but all
|
||||||
|
bus routes are bidirectional, since buses travel in both directions along each
|
||||||
|
route.
|
||||||
|
|
||||||
|
The first line of input has an integer n that is the number of road segments
|
||||||
|
between consecutive stops. The next n lines of input describe the consecutive
|
||||||
|
stops, each of which is named by a string of alphabetical characters with no
|
||||||
|
spaces. The line will contain both of the stops of the segment, separated by a
|
||||||
|
space.
|
||||||
|
|
||||||
|
The final line of input is the query, which is two stop names separated by a
|
||||||
|
space.
|
||||||
|
|
||||||
|
## Output
|
||||||
|
|
||||||
|
Give a sequence of stop names starting at the starting stop of the query and
|
||||||
|
ending at the ending stop separated by spaces (if one exists), or output "no
|
||||||
|
route possible" if none is found.
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>Sample Input</td>
|
||||||
|
<td>Sample Output</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><pre>
|
||||||
|
2
|
||||||
|
Hburg JMU
|
||||||
|
Charlottesville JMU
|
||||||
|
Hburg Charlottesville</pre></td>
|
||||||
|
<td><pre>Hburg JMU Charlottesville</pre></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><pre>
|
||||||
|
4
|
||||||
|
Hburg JMU
|
||||||
|
JMU Upark
|
||||||
|
Upark Hburg
|
||||||
|
CVO UVA
|
||||||
|
Hburg UVA</pre></td>
|
||||||
|
<td><pre>no route possible</pre></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
Larger example files (the one used on Gradescope) can be downloaded here:
|
||||||
|
bus_stops_t3_in.txt Download bus_stops_t3_in.txt bus_stops_t3_exp.txt Download
|
||||||
|
bus_stops_t3_exp.txt Depending how you implement your graph, the order of stops
|
||||||
|
on the path may not be unique, so, checking your solution requires writing a bit
|
||||||
|
more code to validate your path (and of course, that is optional).
|
||||||
|
|
||||||
|
## Hints:
|
||||||
|
|
||||||
|
- Store the stops in a dictionary and use a set for the edge list.
|
||||||
|
- You will need to implement a method that uses a spanning tree. The lab is
|
||||||
|
asking you for a path between the locations (not necessarily the shortest
|
||||||
|
path).
|
||||||
|
|
||||||
|
## Submission:
|
||||||
|
|
||||||
|
Your code in a file name `cs412_bus_stops_a.py` and submit your code to
|
||||||
|
gradescope.
|
||||||
|
|
||||||
|
## Rubrics:
|
||||||
|
|
||||||
|
- (6 points) program solves the examples
|
||||||
|
- (3 points) solves the hidden examples
|
||||||
|
- (1 point) instructor review
|
||||||
BIN
Graph-Search/assets/graph.png
Normal file
BIN
Graph-Search/assets/graph.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 61 KiB |
55
Graph-Search/class-activity.md
Normal file
55
Graph-Search/class-activity.md
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
# Class Activity: Exploring MST Characteristics
|
||||||
|
|
||||||
|
We will be exploring creating graphs that showcase different aspects of MST
|
||||||
|
algorithms.
|
||||||
|
|
||||||
|
## Task 1:
|
||||||
|
|
||||||
|
True/False. A MST can sometimes contain a cycle. Justify your answer with one or
|
||||||
|
two sentences.
|
||||||
|
|
||||||
|
> Answer: False. By definition you can remove an edge from cycle and all
|
||||||
|
> vertices will still be connected. Therefor if the MST has a cycle in it then
|
||||||
|
> that would mean it has a redundant edge and is therefore not minimal.
|
||||||
|
|
||||||
|
## Task 2:
|
||||||
|
|
||||||
|
For the following graph, show the safe data structure after one call to
|
||||||
|
Borůvka's `AddAllSafeEdges` method. Draw the spanning tree F showing all of the
|
||||||
|
safe edges that were added to it after this first pass.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Task 3:
|
||||||
|
|
||||||
|
Create a graph with at least 6 nodes that is fully connected (a complete graph)
|
||||||
|
and has distinct edge weights that Boruvka's algorithm can solve in a single
|
||||||
|
call to `AddAllSafeEdges`. Submit a picture of the graph and the safe array/list
|
||||||
|
that is created by the call to `AddAllSafeEdges`.
|
||||||
|
|
||||||
|
## Task 4:
|
||||||
|
|
||||||
|
Create a graph with at least 6 vertices with distinct edge weights such that the
|
||||||
|
MST contains the edge with the largest weight. Submit a picture of this graph.
|
||||||
|
|
||||||
|
## Task 5:
|
||||||
|
|
||||||
|
Create a graph with at least 4 nodes that is fully connected (a complete graph)
|
||||||
|
and has distinct edge weights that Boruvka's algorithm exhibits its worst case
|
||||||
|
performance. First, define what the worst case perform is for a single call to
|
||||||
|
`AddAllSafeEdges`. Next, showcase this performance on your graph clearly showing
|
||||||
|
how the MST (F) looks after a single call to `AddAllSafeEdges` and showing the
|
||||||
|
contents of the array/list **safe**.
|
||||||
|
|
||||||
|
## Task 6:
|
||||||
|
|
||||||
|
Can you create a graph with at least 4 vertices with distinct edge weights such
|
||||||
|
that the MST does not contain the lightest edge? Explain your answer both
|
||||||
|
logically (1 to 2 sentences) and using the logic within Boruvka's
|
||||||
|
`AddAllSafeEdges`.
|
||||||
|
|
||||||
|
## Submission
|
||||||
|
|
||||||
|
Submit a picture of your answers to this canvas assignment. If you worked with
|
||||||
|
another person, acknowledge that by placing the names of all people you worked
|
||||||
|
with at the top of the first page of your submission.
|
||||||
66
Graph-Search/cs412_bus_stops_a.py
Normal file
66
Graph-Search/cs412_bus_stops_a.py
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
"""
|
||||||
|
name: Nicholas Tamassia
|
||||||
|
|
||||||
|
Honor Code and Acknowledgments:
|
||||||
|
|
||||||
|
This work complies with the JMU Honor Code.
|
||||||
|
|
||||||
|
Comments here on your code and submission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from collections import defaultdict, deque
|
||||||
|
|
||||||
|
|
||||||
|
def whatever_first_search(graph: dict[str, set[str]], start: str, stop: str):
|
||||||
|
|
||||||
|
bag: deque[tuple[str | None, str]] = deque()
|
||||||
|
bag.append((None, start))
|
||||||
|
visited: set[str] = set()
|
||||||
|
parent: dict[str, str | None] = {}
|
||||||
|
|
||||||
|
while len(bag) != 0:
|
||||||
|
parent_node, u = bag.pop()
|
||||||
|
if u in visited:
|
||||||
|
continue
|
||||||
|
visited.add(u)
|
||||||
|
parent[u] = parent_node
|
||||||
|
|
||||||
|
if u == stop:
|
||||||
|
path: list[str] = []
|
||||||
|
while u is not None:
|
||||||
|
path.append(u)
|
||||||
|
u = parent[u]
|
||||||
|
path.reverse()
|
||||||
|
return path
|
||||||
|
|
||||||
|
for v in graph[u]:
|
||||||
|
if v not in visited:
|
||||||
|
bag.append((u, v))
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
# 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())
|
||||||
|
|
||||||
|
graph: dict[str, set[str]] = defaultdict(set)
|
||||||
|
|
||||||
|
for _ in range(0, n):
|
||||||
|
u, v = input().split()
|
||||||
|
graph[u].add(v)
|
||||||
|
graph[v].add(u)
|
||||||
|
|
||||||
|
target = input().split()
|
||||||
|
|
||||||
|
path = whatever_first_search(graph, target[0], target[1])
|
||||||
|
|
||||||
|
if path is None:
|
||||||
|
print("no route possible")
|
||||||
|
else:
|
||||||
|
print(" ".join(path))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
4
Graph-Search/inputs/sample1.txt
Normal file
4
Graph-Search/inputs/sample1.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
2
|
||||||
|
Hburg JMU
|
||||||
|
Charlottesville JMU
|
||||||
|
Hburg Charlottesville
|
||||||
6
Graph-Search/inputs/sample2.txt
Normal file
6
Graph-Search/inputs/sample2.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
4
|
||||||
|
Hburg JMU
|
||||||
|
JMU Upark
|
||||||
|
Upark Hburg
|
||||||
|
CVO UVA
|
||||||
|
Hburg UVA
|
||||||
68419
Graph-Search/inputs/sample3.txt
Normal file
68419
Graph-Search/inputs/sample3.txt
Normal file
File diff suppressed because it is too large
Load Diff
97
Greedy-Algorithms/README.md
Normal file
97
Greedy-Algorithms/README.md
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
# Greedy Algorithms
|
||||||
|
|
||||||
|
The _knapsack_ problem is a well known, that when solved with a backtracking
|
||||||
|
approach, has **exponential** complexity. In this problem, you are given n
|
||||||
|
items, each with item having a dollar value and a weight. The knapsack can carry
|
||||||
|
a maximum weight _W_. The algorithm selects the subset of items with **maximum**
|
||||||
|
value where the sum of the weights of the items does not exceed W.
|
||||||
|
|
||||||
|
## Task 1: Develop pseudocode for 0-1 Knapsack
|
||||||
|
|
||||||
|
Develop pseudocode that solves this version of the knapsack problem (commonly
|
||||||
|
known as the 0-1 knapsack problem, since you either take an item or do not take
|
||||||
|
an item). This should be a backtracking approach with recursion.
|
||||||
|
|
||||||
|
**Submit** this as a text file (`cs412_knapsack_01pseudo.txt`). It is OK if this
|
||||||
|
code does not compile, but it should be fairly close.
|
||||||
|
|
||||||
|
## Task 2: Code fractional knapsack problem
|
||||||
|
|
||||||
|
The fractional knapsack problem allows items to be broken into smaller pieces so
|
||||||
|
that the total value of the items in the knapsack can be maximized as discussed
|
||||||
|
in class. Your method should run in O(n lg n) time. **Submit** this as a text
|
||||||
|
file (`cs412_knapsack_fractional.py`).
|
||||||
|
|
||||||
|
### Input
|
||||||
|
|
||||||
|
Your input will begin with a single line containing a nonnegative integer weight
|
||||||
|
w. This is followed by a single line contains a nonnegative integer n followed
|
||||||
|
by exactly n lines each of which contains a triple (String, real, real). The
|
||||||
|
first String is the item's name, the second is the dollar value of the item and
|
||||||
|
the third is the item's weight.
|
||||||
|
|
||||||
|
### Output
|
||||||
|
|
||||||
|
You should output exactly 2 lines. The first line is the list of items in the
|
||||||
|
knapsack with the value and amount (weight) of each item formatted as shown. The
|
||||||
|
order of the items should follow the ratio of the most expensive per unit
|
||||||
|
weight. The second line is the total value of the items in the knapsack.
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>Sample Input</td>
|
||||||
|
<td>Sample Output</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><pre>
|
||||||
|
11
|
||||||
|
3
|
||||||
|
ring 100 5
|
||||||
|
gold 50 10
|
||||||
|
silver 50 5</pre>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<pre>
|
||||||
|
ring(100.00, 5.00) silver(50.00, 5.00) gold(5.00, 1.00)
|
||||||
|
155.0</pre>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
**Hints:**
|
||||||
|
|
||||||
|
- Python's sort function accepts a lambda function to select which item to use
|
||||||
|
in sorting. So, if you read in the input as a list of lists, it is possible to
|
||||||
|
run the sort on this with a single line of code.
|
||||||
|
|
||||||
|
## Task 3: Reflect on these problems
|
||||||
|
|
||||||
|
Reflect on the problems in task 1 and task 2.
|
||||||
|
|
||||||
|
- Which problem is more efficient to solve, the 0/1 knapsack or fractional
|
||||||
|
knapsack? Use big-OH complexity to justify your answer.
|
||||||
|
- How would the greedy item selection strategy work for the 0/1 knapsack
|
||||||
|
problem? Reflect on 1) whether this approach would yield the optimal answer
|
||||||
|
and 2) How would its the approaches efficiency/speed be impacted (when
|
||||||
|
compared to the backtracking 0/1 approach)?
|
||||||
|
- For the two questions above, give an example input to support your
|
||||||
|
description? Your input should be specified in the same format as the input to
|
||||||
|
Task 2.
|
||||||
|
|
||||||
|
Attach the answers to these question at the end of `cs412_knapsack_01pseudo.txt`
|
||||||
|
file.
|
||||||
|
|
||||||
|
## Rubric:
|
||||||
|
|
||||||
|
- 3 points -- Pseudo code for 0-1 knapsack problem
|
||||||
|
- 5 points -- code runs and passes Gradescope tests
|
||||||
|
- 3 points -- answers to reflection questions in Task 3
|
||||||
|
- 1 point -- instructor review
|
||||||
|
|
||||||
|
## Submit
|
||||||
|
|
||||||
|
Submit both files to Gradescope.
|
||||||
|
|
||||||
|
- `cs412_knapsack_01pseudo.txt` (submit this to Gradescope too and it should
|
||||||
|
contain both Task 1 and Task 3 work)
|
||||||
|
- `cs412_knapsack_fractional.py`
|
||||||
46
Greedy-Algorithms/cs412_knapsack_01pseudo.txt
Normal file
46
Greedy-Algorithms/cs412_knapsack_01pseudo.txt
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
# Task 1:
|
||||||
|
|
||||||
|
```
|
||||||
|
def knapsack(capacity, weights, values, index):
|
||||||
|
if index == 0 or capacity == 0:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if weights[index - 1] > capacity:
|
||||||
|
return knapsack(capacity, weights, values, n - 1)
|
||||||
|
|
||||||
|
skip_item = knapsack(capacity, weights, values, n - 1)
|
||||||
|
|
||||||
|
take_item = values[n - 1] + knapsack(capacity, weights, values, n - 1)
|
||||||
|
|
||||||
|
return max(skip_item, take_item)
|
||||||
|
```
|
||||||
|
|
||||||
|
# Task 3:
|
||||||
|
|
||||||
|
- Fractional which has a worst case runtime of O(nlogn) compared to the
|
||||||
|
backtracking 0/1 knapsack solution which is exponential or O(2^n)
|
||||||
|
|
||||||
|
- The greedy strategy for the 0/1 knapsack problems would still select the items
|
||||||
|
in order of best value-to-weight ratio, but instead of "cutting" once the
|
||||||
|
availabe capacity was less than the weight of the next item, it would just
|
||||||
|
skip that item.
|
||||||
|
|
||||||
|
1. This approach would not always reach the optimal result.
|
||||||
|
2. The speed of the algorithm would be greatly improved from and exponential
|
||||||
|
runtime to a linear-logarithmic runtime.
|
||||||
|
|
||||||
|
- The following example input would produce a suboptimal solution with the
|
||||||
|
greedy approach to the 0/1 knapsack problem. This is because It would take the
|
||||||
|
first item which has the greates value-to-weight ratio (3) leaving it with a
|
||||||
|
remaining capacity of 4 and unable to choose any of the remaining, leaving us
|
||||||
|
with a final value of 18. The optimal solution in this case is to take the
|
||||||
|
latter two for a total value of 20 and 0 remaining capacity.
|
||||||
|
|
||||||
|
```
|
||||||
|
10
|
||||||
|
3
|
||||||
|
A 18 6
|
||||||
|
B 14 5
|
||||||
|
C 6 5
|
||||||
|
```
|
||||||
78
Greedy-Algorithms/cs412_knapsack_fractional.py
Normal file
78
Greedy-Algorithms/cs412_knapsack_fractional.py
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
"""
|
||||||
|
name: Nicholas Tamassia
|
||||||
|
|
||||||
|
Honor Code and Acknowledgments:
|
||||||
|
|
||||||
|
This work complies with the JMU Honor Code.
|
||||||
|
|
||||||
|
Comments here on your code and submission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Self, override
|
||||||
|
|
||||||
|
|
||||||
|
# I don't like tuples, dataclasses ftw
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class KnapsackItem:
|
||||||
|
name: str
|
||||||
|
value: float
|
||||||
|
weight: float
|
||||||
|
|
||||||
|
@property
|
||||||
|
def value_ratio(self) -> float:
|
||||||
|
return self.value / self.weight
|
||||||
|
|
||||||
|
@override
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return f"{self.name}({self.value:.2f}, {self.weight:.2f})"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def parse(cls, string: str) -> Self:
|
||||||
|
values: list[str] = string.strip().split()
|
||||||
|
name, value, weight = values[0], float(values[1]), float(values[2])
|
||||||
|
return cls(name, value, weight)
|
||||||
|
|
||||||
|
|
||||||
|
def knapsack(
|
||||||
|
capacity: int, items: list[KnapsackItem]
|
||||||
|
) -> tuple[list[KnapsackItem], float]:
|
||||||
|
|
||||||
|
current_capacity: float = float(capacity)
|
||||||
|
sorted_items: list[KnapsackItem] = sorted(items, key=lambda i: -i.value_ratio)
|
||||||
|
|
||||||
|
total_value: float = 0.0
|
||||||
|
fractional_items: list[KnapsackItem] = []
|
||||||
|
|
||||||
|
for item in sorted_items:
|
||||||
|
if abs(current_capacity) < 1e-8:
|
||||||
|
break
|
||||||
|
|
||||||
|
name, value, weight = item.name, item.value, item.weight
|
||||||
|
if current_capacity < weight:
|
||||||
|
value = item.value_ratio * current_capacity
|
||||||
|
weight = current_capacity
|
||||||
|
|
||||||
|
fractional_items.append(KnapsackItem(name, value, weight))
|
||||||
|
total_value += value
|
||||||
|
current_capacity -= weight
|
||||||
|
|
||||||
|
return fractional_items, total_value
|
||||||
|
|
||||||
|
|
||||||
|
# All modules for CS 412 must include a main method that allows it
|
||||||
|
# to imported and invoked from other python scripts
|
||||||
|
def main():
|
||||||
|
capacity: int = int(input())
|
||||||
|
n: int = int(input())
|
||||||
|
|
||||||
|
items: list[KnapsackItem] = [KnapsackItem.parse(input()) for _ in range(0, n)]
|
||||||
|
|
||||||
|
fractional_items, total_value = knapsack(capacity, items)
|
||||||
|
|
||||||
|
print(" ".join(map(str, fractional_items)))
|
||||||
|
print(total_value)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
5
Greedy-Algorithms/inputs/sample1.txt
Normal file
5
Greedy-Algorithms/inputs/sample1.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
11
|
||||||
|
3
|
||||||
|
ring 100 5
|
||||||
|
gold 50 10
|
||||||
|
silver 50 5
|
||||||
65
NP-Complete/README.md
Normal file
65
NP-Complete/README.md
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
# Lab 17: NP-Completeness
|
||||||
|
|
||||||
|
## NP-Complete Problems
|
||||||
|
|
||||||
|
### Part A: Unsatisfiable 3-SAT (3 points)
|
||||||
|
|
||||||
|
Answer the following questions:
|
||||||
|
|
||||||
|
- Given a 3SAT problem with 3 binary variables (x1, x2, and x3), what is the min number of clauses that you would need to create an unsatisfiable sentence?
|
||||||
|
- Create a 3SAT sentence with 3 binary variable that is not satisfiable using the minimum number of clauses identified in part 1.
|
||||||
|
|
||||||
|
Turn these in directly here in Canvas.
|
||||||
|
|
||||||
|
### Part B -- Python's Itertools (3 points)
|
||||||
|
|
||||||
|
Python provides a powerful package called itertools (see this webpage for details: [https://docs.python.org/3/library/itertools.html](https://docs.python.org/3/library/itertools.html)). This package provides an easy and memory efficient method of producing iterables that represent combination or permutations of items.
|
||||||
|
|
||||||
|
- Construct a python program that uses the itertools function to print out all possible assignments of a 3 boolean variables. You will need to use the product function within itertools. If you need more help with the product function, look up itertools' documentation on the web.
|
||||||
|
- Augment your python program so that it contains a function that accepts 3 variables (x1, x2, and x3). This function must return the truth value of the 3-SAT sentence you created in Part A:2 (should just be a return statement evaluating the expression). Call this function with all the possible settings and show that your sentence is indeed not satisfiable. In other words, if your function returns true, print the settings that produced a true answer, otherwise print a message verifying that it always returned false.
|
||||||
|
- Create another function that is a copy of the one created in #2 but is missing one of the 3-SAT clauses. Using a similar for loop to #2, call this function with all possible settings of the boolean variables and print out the case where it finds a true assignment (a satisfying assignment).
|
||||||
|
|
||||||
|
Attach this program to this assignment, call it: `cs412_np_3satcheck.py`
|
||||||
|
|
||||||
|
### Part C -- Practice Reduction (3 points)
|
||||||
|
|
||||||
|
Given the following 3-SAT sentence, construct the graph that would serve as input to the independent set problem.
|
||||||
|
|
||||||
|
$(a\lor{b}\lor{c})\land(\lnot{a}\lor{b}\lor{c})\land(a\lor\lnot{b}\lor{c})\land(\lnot{a}\lor\lnot{b}\lor\lnot{c})$
|
||||||
|
|
||||||
|
- Draw this graph on a piece of paper and take a picture of it (and attach it to this assignment).
|
||||||
|
- Write a brief (one sentence) description of the decision problem that needs to be solved by the independent set problem to show that this 3-SAT sentence has a valid assignment.
|
||||||
|
- Identify the largest independent set in the graph created in step #1. If you select the variable "a" that is in the first clause, write that down as A1 (where the one identifies the clause that the variable came from). You can write this on your piece of paper with the graph (just make sure it is included in the picture) OR you can write it in the response section in Canvas.
|
||||||
|
|
||||||
|
### Part D -- Prove that Independent Set is in NP (3 points)
|
||||||
|
|
||||||
|
Write a program that accepts a description of an undirected graph G and a list of vertices and verifies (in polynomial time) that the list of vertices is indeed an independent set).
|
||||||
|
|
||||||
|
Here is some example input:
|
||||||
|
|
||||||
|
```
|
||||||
|
4
|
||||||
|
0 1
|
||||||
|
1 0 3
|
||||||
|
2
|
||||||
|
3 1
|
||||||
|
0 3 2
|
||||||
|
```
|
||||||
|
|
||||||
|
The first line tells you how many vertices are in the graph G. The lines that follow contain the edge list for edge vertex. Finally, a proposed independent set is listed. Your program should output TRUE if the set listed in an independent set or FALSE if it is not an independent set (for example, 0 1 should not be an independent set).
|
||||||
|
|
||||||
|
Attached this program to this assignment and call it `cs412_np_independent_set.py`
|
||||||
|
|
||||||
|
### Part E -- Show that Hamiltonian Paths are NP-Complete (3 points)
|
||||||
|
|
||||||
|
In this section, you need to construct the required components to show that finding a Hamiltonian path is NP-Complete (there are 3 components). Illustrate the reduction sequence as we did on the slides (as a picture) and using the <=p syntax and the boxes. **Note: You do NOT need to show the steps required to change the input (the reduction), just the sketch of the order and what would need to be accomplished.** For the other steps, write a sentence or two and if necessary, argue that these steps can be accomplished in polynomial time.
|
||||||
|
|
||||||
|
Submit these items in a section labels Part E in your writeup.
|
||||||
|
|
||||||
|
### What and Where to Submit:
|
||||||
|
|
||||||
|
Submit the following 3 files here in Canvas:
|
||||||
|
|
||||||
|
- `cs412_np_3satcheck.py`
|
||||||
|
- `cs412_np_independent_set.py`
|
||||||
|
- `cs412_nplab.pdf`
|
||||||
51
NP-Complete/cs412_np_3satcheck.py
Normal file
51
NP-Complete/cs412_np_3satcheck.py
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
"""
|
||||||
|
name: Nicholas Tamassia
|
||||||
|
|
||||||
|
Honor Code and Acknowledgments:
|
||||||
|
|
||||||
|
This work complies with the JMU Honor Code.
|
||||||
|
|
||||||
|
Comments here on your code and submission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from itertools import product
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
|
type Three_SAT = Callable[[bool, bool, bool], bool]
|
||||||
|
|
||||||
|
|
||||||
|
def three_sat_missing(x1: bool, x2: bool, x3: bool) -> bool:
|
||||||
|
combos = product([x1, not x1], [x2, not x2], [x3, not x3])
|
||||||
|
# Remove the last clause
|
||||||
|
missing_combo = list(combos)[:-1]
|
||||||
|
return all(a or b or c for a, b, c in missing_combo)
|
||||||
|
|
||||||
|
|
||||||
|
def three_sat(x1: bool, x2: bool, x3: bool) -> bool:
|
||||||
|
return all(
|
||||||
|
a or b or c for a, b, c in product([x1, not x1], [x2, not x2], [x3, not x3])
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# All modules for CS 412 must include a main method that allows it
|
||||||
|
# to imported and invoked from other python scripts
|
||||||
|
def main():
|
||||||
|
settings = product([False, True], repeat=3)
|
||||||
|
|
||||||
|
sentences_satisfied: dict[Three_SAT, bool] = {}
|
||||||
|
sentences_satisfied[three_sat] = False
|
||||||
|
sentences_satisfied[three_sat_missing] = False
|
||||||
|
|
||||||
|
for setting in settings:
|
||||||
|
for sentence in sentences_satisfied:
|
||||||
|
if sentence(*setting):
|
||||||
|
print(f"{setting} satisfied {sentence.__name__}")
|
||||||
|
sentences_satisfied[sentence] = True
|
||||||
|
|
||||||
|
for sentence, satisfied in sentences_satisfied.items():
|
||||||
|
if not satisfied:
|
||||||
|
print(f"{sentence.__name__} is not satisfiable")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
38
NP-Complete/cs412_np_independent_set.py
Normal file
38
NP-Complete/cs412_np_independent_set.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
"""
|
||||||
|
name: Nicholas Tamassia
|
||||||
|
|
||||||
|
Honor Code and Acknowledgments:
|
||||||
|
|
||||||
|
This work complies with the JMU Honor Code.
|
||||||
|
|
||||||
|
Comments here on your code and submission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def independent_set(proposed_set: set[int], graph: dict[int, set[int]]) -> bool:
|
||||||
|
for vertex in proposed_set:
|
||||||
|
for adjecent in graph[vertex]:
|
||||||
|
if adjecent in proposed_set:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
# 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(input())
|
||||||
|
|
||||||
|
graph: dict[int, set[int]] = {}
|
||||||
|
|
||||||
|
for _ in range(n):
|
||||||
|
vertecies = list(map(int, input().split()))
|
||||||
|
graph[vertecies[0]] = set(vertecies[1:])
|
||||||
|
|
||||||
|
proposed_set = set(map(int, input().split()))
|
||||||
|
|
||||||
|
print("TRUE" if independent_set(proposed_set, graph) else "FALSE")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
6
NP-Complete/input/sample1.txt
Normal file
6
NP-Complete/input/sample1.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
4
|
||||||
|
0 1
|
||||||
|
1 0 3
|
||||||
|
2
|
||||||
|
3 1
|
||||||
|
0 3 2
|
||||||
@@ -61,3 +61,198 @@ unique palindromic partitions that can be made with the input string.
|
|||||||
that slicing takes 3 values (start pos, end pos, step size). See this website
|
that slicing takes 3 values (start pos, end pos, step size). See this website
|
||||||
for details:
|
for details:
|
||||||
https://www.digitalocean.com/community/tutorials/how-to-index-and-slice-strings-in-python-3
|
https://www.digitalocean.com/community/tutorials/how-to-index-and-slice-strings-in-python-3
|
||||||
|
|
||||||
|
# Lab 6: Coding Palindrome Partitioning with Memoization
|
||||||
|
|
||||||
|
**NOTE**: This lab is meant to be started and mainly completed in class.
|
||||||
|
|
||||||
|
For this lab you will modify your solution (or the sample solution) from Lab 4:
|
||||||
|
Coding Palindrone Partitioning with Backtracking so that it doesn't run in
|
||||||
|
exponential time. To do this , you will employ **Memoization**! You must
|
||||||
|
manually program the memoization and NOT use any Python features (like those in
|
||||||
|
functools) to accomplish this task.
|
||||||
|
|
||||||
|
The backtracking algorithm ends up repeating the same computation over and over
|
||||||
|
again, because the recursive algorithm is often invoked on the same input
|
||||||
|
string. For any but the shortest strings, this adds up very, very fast and the
|
||||||
|
algorithm becomes unusable. In this lab we will modify the algorithm so that it
|
||||||
|
uses a memoization structure to memoize any answers it computes.
|
||||||
|
|
||||||
|
Before you start working on the code, just as a sanity check, try to run your
|
||||||
|
code from last week on the following input and see if your code terminates
|
||||||
|
within the next minute.
|
||||||
|
|
||||||
|
```
|
||||||
|
1
|
||||||
|
eefffefeeefffefeffefefeeefefffefefefe
|
||||||
|
```
|
||||||
|
|
||||||
|
Your solution should return 8931805.
|
||||||
|
|
||||||
|
Now try it with a string that is just 3 characters longer:
|
||||||
|
|
||||||
|
```
|
||||||
|
1
|
||||||
|
abcdefghijklmnopqrstuvwxyzabcdefghijklmn
|
||||||
|
```
|
||||||
|
|
||||||
|
Why is it that this solution runs so quickly? (We will discuss in class).
|
||||||
|
|
||||||
|
And finally one that is just a little longer than that. Actually, you will
|
||||||
|
probably need to hit Ctrl-C to kill this one after a few minutes (as I do not
|
||||||
|
believe it will finish in any reasonable amount of time).
|
||||||
|
|
||||||
|
```
|
||||||
|
1
|
||||||
|
eefffefeeefffefeffefefeeefefffefefefefefefffffffefeeee
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 1: Introduce a memoization structure to improve performance
|
||||||
|
|
||||||
|
The input to our recursive algorithm is a string. The output is an integer.
|
||||||
|
Thus, for any input string, we would like to memoize the resulting integer. The
|
||||||
|
most natural quick-and-dirty way to do this is to use a hashmap/dictionary, that
|
||||||
|
maps input strings to output integers. Python implements hashmaps as first class
|
||||||
|
objects in python dictionaries.
|
||||||
|
|
||||||
|
**Task 1**: Modify your algorithm so that it uses a dictionary to cache any
|
||||||
|
computed solutions immediately before the solution is returned. The algorithm
|
||||||
|
should pre-empt the recursive algorithm by checking whether the solution is
|
||||||
|
already in the dictionary. If it is, then return the result immediately rather
|
||||||
|
than running the recursive algorithm.
|
||||||
|
|
||||||
|
**Verify your results**: Run your modified code on the input above (the one that
|
||||||
|
would never finish). You should get 82654655060 possible ways of subdividing the
|
||||||
|
string. That was fast too, wasn't it? Unless you killed it, your other solution
|
||||||
|
is still running (and will be until the heat death of the universe...).
|
||||||
|
|
||||||
|
Name your code `cs412_palindrome_partition_memoized_a.py` and turn it in into
|
||||||
|
Gradescope
|
||||||
|
|
||||||
|
## Step 2: Stop using strings as inputs.
|
||||||
|
|
||||||
|
Ok. This is going to be a bit trickier conceptually. Your recursive algorithm,
|
||||||
|
right now, is taking a string as input and then slicing and dicing it to produce
|
||||||
|
inputs for the recursive calls. This is fine and works in this case on the
|
||||||
|
inputs we've tested, but one of the great things about memoization and dynamic
|
||||||
|
programming is that it allows us to completely eliminate recursion. This is
|
||||||
|
important, because many languages (like Python) have a maximum recursion depth
|
||||||
|
that is fairly limited (for example, Python's is usually 1,000 and Java's is
|
||||||
|
1024). This means that if you wanted to run your algorithm on a string of size
|
||||||
|
1025 in Java, it would get a stack overflow (Haskell, on the other hand, has an
|
||||||
|
infinite stack, maybe you shouldn't be so happy programming Java and Python all
|
||||||
|
the time...).
|
||||||
|
|
||||||
|
It is a little tricky to use dynamic programming on a recursive algorithm that
|
||||||
|
is splitting strings to produce the next subproblem's input. (Or, similarly, if
|
||||||
|
your algorithm is working on a list and you are creating sublists, a string is
|
||||||
|
just a fancy list of characters after all.) It turns out to be **much easier**
|
||||||
|
to convert a memoized recursive algorithm that operates on a string or list to a
|
||||||
|
loop if we **never split the string or list into smaller pieces.**
|
||||||
|
|
||||||
|
_But how can we do that?_ You are thinking it, I know it.
|
||||||
|
|
||||||
|
Think of the following. Suppose you have a string s = "abcdef" and you want to
|
||||||
|
pass the substring "cdef" beginning at index 2. You could create the string
|
||||||
|
"cdef" explicitly using a substring method and pass the string to the next call.
|
||||||
|
**Or you could decide to be clever**. Instead of passing the substring, just
|
||||||
|
pass the string s again (or make it a global string defined in an outer
|
||||||
|
function) and an index representing the substring you want. So instead of
|
||||||
|
passing "cdef", you just pass the index 2. Your algorithm would then use index 2
|
||||||
|
implicitly to mean the substring of the original string s beginning at index 2.
|
||||||
|
|
||||||
|
This way your algorithm is really using an _integer_ as input to the recursive
|
||||||
|
calls of the algorithm. The original string remains unmodified throughout the
|
||||||
|
algorithm and is really just a constant. You can pass a reference to this
|
||||||
|
constant to each recursive call, or you can create an outer function and define
|
||||||
|
it (see the example below), or set up a class that just stores it as member
|
||||||
|
data, or something like that. It doesn't matter how you do it, you just need to
|
||||||
|
get it into your head that the variable part of the input to the recursive part
|
||||||
|
of your algorithm is _no longer the string but is instead the integer_. Leave
|
||||||
|
the string alone. You'll thank me later.
|
||||||
|
|
||||||
|
**Task 2: Now for the hard work**. Modify your solution above so that you track
|
||||||
|
substrings _implicitly_ by passing integers as input representing the start of
|
||||||
|
the substring _instead of passing the substrings themselves_. You may pass a
|
||||||
|
reference to the input string as well (i.e. avoid making it global), but every
|
||||||
|
recursive call should get the same string. To get credit for this portion of the
|
||||||
|
lab, you must:
|
||||||
|
|
||||||
|
- Use an index to access the memoized structure (in other words, do NOT use a
|
||||||
|
string)
|
||||||
|
- your recursive function should accept the index to the string as the argument
|
||||||
|
(and not a substring).
|
||||||
|
- To receive full credit, you need to use a list to store the memoized data (and
|
||||||
|
not a dictionary)
|
||||||
|
|
||||||
|
Python allows you to create **nested functions** which can be very helpful in
|
||||||
|
these situations. Take the following example:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def countPalindroneParts(input_string):
|
||||||
|
def countPP(i):
|
||||||
|
# your recursive code here
|
||||||
|
return something
|
||||||
|
|
||||||
|
return countPP(0) # first call to inner method
|
||||||
|
```
|
||||||
|
|
||||||
|
This will set you up for our next week of dynamic programming, where we replace
|
||||||
|
recursion with iteration like the monsters we are.
|
||||||
|
|
||||||
|
Name your modified code `cs412_palindrome_partition_memoized_b.py` and turn it
|
||||||
|
in on Gradescope. It should behave the same as your first solution except it
|
||||||
|
must use a nested function and not pass any strings in the recursive calls.
|
||||||
|
|
||||||
|
## Turn It In:
|
||||||
|
|
||||||
|
Submit both `cs412_palindrome_partition_memoized_a.py` (5 points) and
|
||||||
|
`cs412_palindrome_partition_memorized_b.py` (5 points) to Gradescope by the due
|
||||||
|
date.
|
||||||
|
|
||||||
|
# Lab 7: Dynamic Programming
|
||||||
|
|
||||||
|
## Dynamic Programming Solution to Palindromic Partitions
|
||||||
|
|
||||||
|
Your first task is to modify your solution (or the sample solution) from Lab 6:
|
||||||
|
Coding Palindrome Partitioning with Memoization so that your algorithm no longer
|
||||||
|
uses recursion, but instead fills the memoization structure via a loop. To do
|
||||||
|
this, follow the steps outlined below.
|
||||||
|
|
||||||
|
1. The memoization structure from the previous lab is a 1D array. Your first
|
||||||
|
task is to figure out which direction the recursive algorithm fills the
|
||||||
|
array. This is determined by how our recursive subproblems are generated.
|
||||||
|
- If, in order to solve a problem on input i, the recursive algorithm
|
||||||
|
recurses on a value larger than i, then determining the solution for i
|
||||||
|
requires that we have already computed solutions for larger values. Thus,
|
||||||
|
we need to loop backwards through the memoization array, since each
|
||||||
|
iteration will fill in a value at a particular index i and need access to
|
||||||
|
already computed values for larger indices. On the other hand, if we
|
||||||
|
recurse on smaller values of i then our loop should iterate forwards
|
||||||
|
through the array. Drawing a diagram of your memoization structure with
|
||||||
|
dependency arrows can help you determine this.
|
||||||
|
2. Your next task is to initialize a memoization structure and write a loop that
|
||||||
|
fills in the memoization structure in the proper direction.
|
||||||
|
3. Next you need to initialize the entries in the memoization data structure
|
||||||
|
that represent base cases.
|
||||||
|
4. Next, modify your recursive algorithm by replacing recursive calls with
|
||||||
|
direct lookups into the memoization structure, and place the code for this
|
||||||
|
into the body of your loop. In other words, your code should **not contain
|
||||||
|
ANY recursive calls.**
|
||||||
|
5. Analyze the (analytical) runtime of your algorithm and put the runtime into
|
||||||
|
the comments section at the top of your program. Your analysis should
|
||||||
|
reference specific lines of code (i.e., the loops on lines xx-yy take these
|
||||||
|
many steps).
|
||||||
|
6. Turn your code in as `cs412_palindrome_dynamic.py`
|
||||||
|
|
||||||
|
Submit your code to Gradescope. Gradescope only checks that the correct solution
|
||||||
|
is achieved. Code that is submitted that does not use dynamic programming (that
|
||||||
|
is, does not fill in an array with the answers) will receive 0 credit.
|
||||||
|
|
||||||
|
## Rubric:
|
||||||
|
|
||||||
|
- (7 points) your code does not use recursion and fills in the memoized
|
||||||
|
structure correctly
|
||||||
|
- (2 points) your comments are at the top of the code and correctly identify the
|
||||||
|
run time of the algorithm.
|
||||||
|
- (1 point) instructor code review
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
"""
|
|
||||||
name: Nicholas Tamassia
|
|
||||||
|
|
||||||
Honor Code and Acknowledgments:
|
|
||||||
|
|
||||||
This work complies with the JMU Honor Code.
|
|
||||||
|
|
||||||
Comments here on your code and submission.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def is_palindrome(s: str) -> bool:
|
|
||||||
return s == s[::-1]
|
|
||||||
|
|
||||||
|
|
||||||
def palindrome_partitions(s: str) -> int:
|
|
||||||
if len(s) == 0:
|
|
||||||
return 1
|
|
||||||
|
|
||||||
sum = 0
|
|
||||||
|
|
||||||
for i in range(1, len(s) + 1):
|
|
||||||
left = s[:i]
|
|
||||||
right = s[i:]
|
|
||||||
|
|
||||||
if is_palindrome(left):
|
|
||||||
sum += palindrome_partitions(right)
|
|
||||||
|
|
||||||
return sum
|
|
||||||
|
|
||||||
|
|
||||||
# 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())
|
|
||||||
|
|
||||||
for i in range(0, n):
|
|
||||||
in_str = input().strip()
|
|
||||||
print(palindrome_partitions(in_str))
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
52
Palindrome-Partitioning/cs412_palindrome_dynamic.py
Normal file
52
Palindrome-Partitioning/cs412_palindrome_dynamic.py
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
"""
|
||||||
|
name: Nicholas Tamassia
|
||||||
|
|
||||||
|
Honor Code and Acknowledgments:
|
||||||
|
|
||||||
|
This work complies with the JMU Honor Code.
|
||||||
|
|
||||||
|
Runtime: O(n^2).
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Analysis:
|
||||||
|
# The loop on line 28 iterates n times where n is the length of the input string.
|
||||||
|
# The loop on line 32 iterates n - i times, which despite being practically fewer
|
||||||
|
# steps on average than the line 28 loop, still scales linearly with the size of
|
||||||
|
# the input string. Because both loops are O(n) and the second is nested in the
|
||||||
|
# first, the overall runtime complexity is O(n^2).
|
||||||
|
|
||||||
|
|
||||||
|
def palindrome_partitions(s: str) -> int:
|
||||||
|
|
||||||
|
n: int = len(s)
|
||||||
|
memo: list[int] = [0] * (n + 1)
|
||||||
|
memo[n] = 1
|
||||||
|
|
||||||
|
def is_palindrome(start: int, end: int) -> bool:
|
||||||
|
return s[start:end] == s[start:end][::-1]
|
||||||
|
|
||||||
|
for i in range(n - 1, -1, -1):
|
||||||
|
|
||||||
|
sum = 0
|
||||||
|
|
||||||
|
for j in range(i + 1, n + 1):
|
||||||
|
if is_palindrome(i, j):
|
||||||
|
sum += memo[j]
|
||||||
|
|
||||||
|
memo[i] = sum
|
||||||
|
|
||||||
|
return memo[0]
|
||||||
|
|
||||||
|
|
||||||
|
# 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())
|
||||||
|
|
||||||
|
for _ in range(0, n):
|
||||||
|
in_str = input().strip()
|
||||||
|
print(palindrome_partitions(in_str))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
"""
|
||||||
|
name: Nicholas Tamassia
|
||||||
|
|
||||||
|
Honor Code and Acknowledgments:
|
||||||
|
|
||||||
|
This work complies with the JMU Honor Code.
|
||||||
|
|
||||||
|
Comments here on your code and submission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def is_palindrome(s: str) -> bool:
|
||||||
|
return s == s[::-1]
|
||||||
|
|
||||||
|
|
||||||
|
def palindrome_partitions(s: str) -> int:
|
||||||
|
|
||||||
|
memo: dict[str, int] = {}
|
||||||
|
|
||||||
|
def recurse(s: str) -> int:
|
||||||
|
nonlocal memo
|
||||||
|
|
||||||
|
if len(s) == 0:
|
||||||
|
return 1
|
||||||
|
|
||||||
|
sum = 0
|
||||||
|
|
||||||
|
for i in range(1, len(s) + 1):
|
||||||
|
left: str = s[:i]
|
||||||
|
right: str = s[i:]
|
||||||
|
|
||||||
|
if is_palindrome(left):
|
||||||
|
if right not in memo:
|
||||||
|
memo[right] = recurse(right)
|
||||||
|
|
||||||
|
sum += memo[right]
|
||||||
|
|
||||||
|
return sum
|
||||||
|
|
||||||
|
return recurse(s)
|
||||||
|
|
||||||
|
|
||||||
|
# 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())
|
||||||
|
|
||||||
|
for _ in range(0, n):
|
||||||
|
in_str = input().strip()
|
||||||
|
print(palindrome_partitions(in_str))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
"""
|
||||||
|
name: Nicholas Tamassia
|
||||||
|
|
||||||
|
Honor Code and Acknowledgments:
|
||||||
|
|
||||||
|
This work complies with the JMU Honor Code.
|
||||||
|
|
||||||
|
Comments here on your code and submission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def is_palindrome(s: str, start: int, end: int) -> bool:
|
||||||
|
return s[start:end] == s[start:end][::-1]
|
||||||
|
|
||||||
|
|
||||||
|
def palindrome_partitions(s: str) -> int:
|
||||||
|
|
||||||
|
memo: list[int] = [-1] * (len(s) + 1)
|
||||||
|
|
||||||
|
def recurse(start: int) -> int:
|
||||||
|
nonlocal memo
|
||||||
|
|
||||||
|
if start == len(s):
|
||||||
|
return 1
|
||||||
|
|
||||||
|
if memo[start] != -1:
|
||||||
|
return memo[start]
|
||||||
|
|
||||||
|
sum = 0
|
||||||
|
|
||||||
|
for i in range(start + 1, len(s) + 1):
|
||||||
|
if is_palindrome(s, start, i):
|
||||||
|
sum += recurse(i)
|
||||||
|
|
||||||
|
memo[start] = sum
|
||||||
|
return sum
|
||||||
|
|
||||||
|
return recurse(0)
|
||||||
|
|
||||||
|
|
||||||
|
# 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())
|
||||||
|
|
||||||
|
for _ in range(0, n):
|
||||||
|
in_str = input().strip()
|
||||||
|
print(palindrome_partitions(in_str))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
2
Palindrome-Partitioning/inputs/sample2.txt
Normal file
2
Palindrome-Partitioning/inputs/sample2.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
1
|
||||||
|
eefffefeeefffefeffefefeeefefffefefefefefefffffffefeeee
|
||||||
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}")
|
||||||
113
Railroad-Construction/cs412_railroad_a.py
Normal file
113
Railroad-Construction/cs412_railroad_a.py
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
"""
|
||||||
|
name: Nicholas Tamassia
|
||||||
|
|
||||||
|
Honor Code and Acknowledgments:
|
||||||
|
|
||||||
|
This work complies with the JMU Honor Code.
|
||||||
|
|
||||||
|
Comments here on your code and submission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
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]]
|
||||||
|
type Edge = tuple[int, 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:
|
||||||
|
parent: dict[int, int] = {v: v for v in graph}
|
||||||
|
rank: dict[int, int] = {v: 0 for v in graph}
|
||||||
|
|
||||||
|
def find(v: int) -> int:
|
||||||
|
if parent[v] != v:
|
||||||
|
parent[v] = find(parent[v])
|
||||||
|
return parent[v]
|
||||||
|
|
||||||
|
def union(u: int, v: int) -> None:
|
||||||
|
root_u, root_v = find(u), find(v)
|
||||||
|
if root_u == root_v:
|
||||||
|
return
|
||||||
|
if rank[root_u] < rank[root_v]:
|
||||||
|
parent[root_u] = root_v
|
||||||
|
elif rank[root_u] > rank[root_v]:
|
||||||
|
parent[root_v] = root_u
|
||||||
|
else:
|
||||||
|
parent[root_v] = root_u
|
||||||
|
rank[root_u] += 1
|
||||||
|
|
||||||
|
num_components = len(graph)
|
||||||
|
mst_edges: list[Edge] = []
|
||||||
|
|
||||||
|
while num_components > 1:
|
||||||
|
cheapest: dict[int, Edge] = {}
|
||||||
|
|
||||||
|
for u in graph:
|
||||||
|
for v, w in graph[u].items():
|
||||||
|
set_u = find(u)
|
||||||
|
set_v = find(v)
|
||||||
|
if set_u == set_v:
|
||||||
|
continue
|
||||||
|
if set_u not in cheapest or cheapest[set_u][2] > w:
|
||||||
|
cheapest[set_u] = (u, v, w)
|
||||||
|
if set_v not in cheapest or cheapest[set_v][2] > w:
|
||||||
|
cheapest[set_v] = (v, u, w)
|
||||||
|
|
||||||
|
for edge in cheapest.values():
|
||||||
|
u, v, w = edge
|
||||||
|
set_u = find(u)
|
||||||
|
set_v = find(v)
|
||||||
|
if set_u == set_v:
|
||||||
|
continue
|
||||||
|
mst_edges.append(edge)
|
||||||
|
union(set_u, set_v)
|
||||||
|
num_components -= 1
|
||||||
|
|
||||||
|
return sum(map(lambda x: x[2], mst_edges))
|
||||||
|
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
cost = calculate_cost(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
|
||||||
@@ -8,10 +8,14 @@ Honor Code and Acknowledgments:
|
|||||||
Comments here on your code and submission.
|
Comments here on your code and submission.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import typing
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
type SquareGrid = np.ndarray[tuple[int, int], np.dtype[np.int64]]
|
||||||
|
|
||||||
def tile(grid: np.ndarray, hole: tuple[int, int]) -> None:
|
|
||||||
|
def tile(grid: SquareGrid, hole: tuple[int, int]) -> None:
|
||||||
|
|
||||||
tile_index = 0
|
tile_index = 0
|
||||||
grid[hole] = -1
|
grid[hole] = -1
|
||||||
@@ -59,14 +63,15 @@ def tile(grid: np.ndarray, hole: tuple[int, int]) -> None:
|
|||||||
# to imported and invoked from other python scripts
|
# to imported and invoked from other python scripts
|
||||||
def main():
|
def main():
|
||||||
n: int = int(input())
|
n: int = int(input())
|
||||||
x, y = map(int, input().split(" "))
|
x, y = map(int, input().split())
|
||||||
|
|
||||||
grid: np.ndarray = np.zeros((2**n, 2**n), dtype=int)
|
grid: SquareGrid = np.zeros((2**n, 2**n), dtype=np.int64)
|
||||||
|
|
||||||
tile(grid, (x, y))
|
tile(grid, (x, y))
|
||||||
|
|
||||||
for row in grid:
|
for row in grid:
|
||||||
print(" ".join(map(lambda x: f"{x:02d}", row)))
|
row_list = typing.cast(list[int], row.tolist())
|
||||||
|
print(" ".join(map(lambda x: f"{x:02d}", row_list)))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
3
Shuffle/inputs/sample1.txt
Normal file
3
Shuffle/inputs/sample1.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
ACF
|
||||||
|
BCF
|
||||||
|
ABCCFF
|
||||||
3
Shuffle/inputs/sample2.txt
Normal file
3
Shuffle/inputs/sample2.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
ACF
|
||||||
|
BCF
|
||||||
|
ABFCCF
|
||||||
62
Shuffle/shuffle.py
Normal file
62
Shuffle/shuffle.py
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
"""
|
||||||
|
name: Nicholas Tamassia
|
||||||
|
|
||||||
|
Honor Code and Acknowledgments:
|
||||||
|
|
||||||
|
This work complies with the JMU Honor Code.
|
||||||
|
|
||||||
|
Comments here on your code and submission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import pprint
|
||||||
|
|
||||||
|
|
||||||
|
def valid_shuffle(a: str, b: str, c: str) -> bool:
|
||||||
|
|
||||||
|
memo: list[list[bool | None]] = [
|
||||||
|
([None] * (len(a) + 1)) for _ in range(0, len(b) + 1)
|
||||||
|
]
|
||||||
|
|
||||||
|
memo[len(a)][len(b)] = True
|
||||||
|
|
||||||
|
def recurse(i: int, j: int) -> bool:
|
||||||
|
if i >= len(a) or j >= len(b):
|
||||||
|
return False
|
||||||
|
|
||||||
|
k: int = i + j
|
||||||
|
if k == (len(c) - 2):
|
||||||
|
return True
|
||||||
|
|
||||||
|
a_val: bool | None = memo[i + 1][j]
|
||||||
|
if a_val is None:
|
||||||
|
a_val = recurse(i + 1, j) if a[i] == c[k] else False
|
||||||
|
memo[i + 1][j] = a_val
|
||||||
|
|
||||||
|
b_val: bool | None = memo[i][j + 1]
|
||||||
|
if b_val is None:
|
||||||
|
b_val = recurse(i, j + 1) if b[j] == c[k] else False
|
||||||
|
memo[i][j + 1] = b_val
|
||||||
|
|
||||||
|
memo[i][j] = a_val or b_val
|
||||||
|
|
||||||
|
return a_val or b_val
|
||||||
|
|
||||||
|
val = recurse(0, 0)
|
||||||
|
|
||||||
|
pprint.pprint(memo)
|
||||||
|
|
||||||
|
return val
|
||||||
|
|
||||||
|
|
||||||
|
# All modules for CS 412 must include a main method that allows it
|
||||||
|
# to imported and invoked from other python scripts
|
||||||
|
def main():
|
||||||
|
a: str = input()
|
||||||
|
b: str = input()
|
||||||
|
c: str = input()
|
||||||
|
|
||||||
|
print(valid_shuffle(a, b, c))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
16
devenv.lock
16
devenv.lock
@@ -3,10 +3,10 @@
|
|||||||
"devenv": {
|
"devenv": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"dir": "src/modules",
|
"dir": "src/modules",
|
||||||
"lastModified": 1757570236,
|
"lastModified": 1762789285,
|
||||||
"owner": "cachix",
|
"owner": "cachix",
|
||||||
"repo": "devenv",
|
"repo": "devenv",
|
||||||
"rev": "c57bded76fa6a885ab1dee2c75216cc23d58b311",
|
"rev": "379dd49a2ae3470e216cf07bfee4326e3c5a5baf",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -19,10 +19,10 @@
|
|||||||
"flake-compat": {
|
"flake-compat": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1747046372,
|
"lastModified": 1761588595,
|
||||||
"owner": "edolstra",
|
"owner": "edolstra",
|
||||||
"repo": "flake-compat",
|
"repo": "flake-compat",
|
||||||
"rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
|
"rev": "f387cd2afec9419c8ee37694406ca490c3f34ee5",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -40,10 +40,10 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1757588530,
|
"lastModified": 1762441963,
|
||||||
"owner": "cachix",
|
"owner": "cachix",
|
||||||
"repo": "git-hooks.nix",
|
"repo": "git-hooks.nix",
|
||||||
"rev": "b084b2c2b6bc23e83bbfe583b03664eb0b18c411",
|
"rev": "8e7576e79b88c16d7ee3bbd112c8d90070832885",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -74,10 +74,10 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1755783167,
|
"lastModified": 1761313199,
|
||||||
"owner": "cachix",
|
"owner": "cachix",
|
||||||
"repo": "devenv-nixpkgs",
|
"repo": "devenv-nixpkgs",
|
||||||
"rev": "4a880fb247d24fbca57269af672e8f78935b0328",
|
"rev": "d1c30452ebecfc55185ae6d1c983c09da0c274ff",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
Reference in New Issue
Block a user