├── .github ├── CONTRIBUTING.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── Guide ├── Constructive │ ├── Bonetrousle │ │ ├── README.md │ │ └── solution.java │ ├── New Year Chaos │ │ ├── README.md │ │ └── solution.cpp │ └── README.md ├── Data Structures │ └── Contacts │ │ ├── README.MD │ │ └── Solution.java ├── Divide and Conquer │ ├── Educational │ │ └── Counting Inversions │ │ │ ├── README.md │ │ │ ├── assets │ │ │ ├── ci_example.png │ │ │ └── merge_sort_pic.png │ │ │ └── solution.py │ ├── Practice │ │ └── README.md │ └── README.md ├── Dynamic Programming │ ├── Educational │ │ ├── Segmented Least Squares │ │ │ ├── README.md │ │ │ ├── assets │ │ │ │ ├── a_and_b.png │ │ │ │ ├── initial_plot.png │ │ │ │ ├── least_squares_sol.png │ │ │ │ ├── opt_j.png │ │ │ │ ├── seg_01.png │ │ │ │ ├── seg_02.png │ │ │ │ ├── seg_03.png │ │ │ │ ├── seg_04.png │ │ │ │ ├── seg_12.png │ │ │ │ ├── seg_13.png │ │ │ │ ├── seg_14.png │ │ │ │ ├── seg_23.png │ │ │ │ ├── seg_24.png │ │ │ │ ├── seg_34.png │ │ │ │ ├── seg_least_squares_sol1.png │ │ │ │ ├── seg_least_squares_sol2.png │ │ │ │ ├── solution.png │ │ │ │ └── sse.png │ │ │ └── solution.py │ │ └── Weighted Interval Scheduling │ │ │ ├── README.md │ │ │ ├── assets │ │ │ ├── empty.png │ │ │ ├── final.png │ │ │ ├── initial1.png │ │ │ └── sorted1.png │ │ │ └── solution.py │ ├── Practice │ │ ├── Abbreviation │ │ │ ├── README.md │ │ │ └── solution.java │ │ ├── Maximum Path Sum │ │ │ ├── README.md │ │ │ └── solution.java │ │ └── Nikita And The Game │ │ │ ├── README.md │ │ │ └── solution.cpp │ └── README.md ├── GraphTheory │ ├── JackGoesToRapture │ │ ├── README.md │ │ └── solution.java │ ├── Journey to the Moon │ │ ├── README.md │ │ └── solution.cpp │ └── README.md ├── Greedy │ ├── Educational │ │ ├── Interval Partitioning │ │ │ ├── README.md │ │ │ ├── assets │ │ │ │ ├── step1.PNG │ │ │ │ ├── step2.PNG │ │ │ │ ├── step3.PNG │ │ │ │ ├── step4.PNG │ │ │ │ ├── step5.PNG │ │ │ │ ├── step6.PNG │ │ │ │ ├── step7.PNG │ │ │ │ ├── step8.PNG │ │ │ │ └── step9.PNG │ │ │ └── solution.py │ │ ├── Interval Scheduling │ │ │ ├── README.md │ │ │ ├── solutionV1.py │ │ │ └── solutionV2.py │ │ ├── Minimizing Maximum Lateness Scheduling │ │ │ ├── README.md │ │ │ └── solution.py │ │ ├── Minimum Spanning Tree │ │ │ ├── DisjointSet.py │ │ │ ├── Graph.py │ │ │ ├── README.md │ │ │ ├── assets │ │ │ │ ├── MST_kruskal_en.gif │ │ │ │ ├── disjoint_set_group.png │ │ │ │ ├── disjoint_set_individual.png │ │ │ │ ├── iter1.png │ │ │ │ ├── iter2.png │ │ │ │ ├── iter3.png │ │ │ │ ├── iter4.png │ │ │ │ ├── iter5.png │ │ │ │ └── iter6.png │ │ │ └── solution.py │ │ ├── Shortest Path │ │ │ ├── FibHeap.py │ │ │ ├── Graph.py │ │ │ ├── README.md │ │ │ ├── assets │ │ │ │ ├── DijkstraDemo.gif │ │ │ │ ├── idg_complete.png │ │ │ │ ├── initial_directed_graph1.png │ │ │ │ ├── initial_undirected_graph1.png │ │ │ │ ├── iug_01.png │ │ │ │ ├── iug_1.png │ │ │ │ ├── iug_2.png │ │ │ │ ├── iug_3.png │ │ │ │ ├── iug_4.png │ │ │ │ ├── iug_5.png │ │ │ │ └── iug_6.png │ │ │ └── solution.py │ │ └── Stable Marriage │ │ │ ├── README.md │ │ │ ├── assets │ │ │ ├── step1.PNG │ │ │ ├── step2.PNG │ │ │ ├── step3.PNG │ │ │ ├── step4.PNG │ │ │ ├── step5.PNG │ │ │ ├── step6.PNG │ │ │ ├── step7.PNG │ │ │ ├── step8.PNG │ │ │ └── step9.PNG │ │ │ └── solution.py │ ├── Practice │ │ ├── Algorithmic Crush │ │ │ ├── README.md │ │ │ └── solution.cpp │ │ └── Kindergarten Adventure │ │ │ ├── README.md │ │ │ ├── optimisedSolution.java │ │ │ └── solution.java │ └── README.md └── README.md ├── README.md └── Templates ├── README.md └── problem-template.md /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | So you want to contribute to this repository? 3 | To do that you need to be a TTU student who is participating in SDC's Applied Algorithms Team. 4 | 5 | **Are you a TTU student who wants to be a part of this but isn't?** 6 | Check us out on Slack at [ttucs.slack.com](https://ttucs.slack.com) in the #AppliedAlgorithms channel. 7 | 8 | Before you can make any changes to this repository, you must first have approval from the maintainers. 9 | 10 | 11 | ## Coding Standards 12 | Try to make your code clean and easy to read with comments as necessary. A `README.md` is required for every problem solution. Please follow the standards put forth in the [Templates](/Templates) folder. 13 | We understand that not all solutions are the very clear at first, but please make the code as clean as possible (within reason). If the reviewers think there can be improvements they will let you know. 14 | 15 | 16 | ## Getting Your Code Picked 17 | So let's say you have a solution you would like to submit to this repository. 18 | **These are the steps to take to getting your solution into this repository.** 19 | 20 | 1. Before anything else, prepare your code for submission. 21 | 1. Double check that it passes **all** test cases. 22 | 2. Remove any unused code. 23 | 3. Comment anything that you did that is "weird" or "tricky". (not intuitive to the average reader) 24 | 2. Create a [Gist](https://gist.github.com/) for your solution. 25 | - Example: [New Year Chaos](https://gist.github.com/asclines/b68eca735d392123d0cde2b343100677) 26 | 3. Once a Call for Submission issue is raised on that problem, you can submit your Gist by commenting a link to that Gist in the issue. 27 | - Example: [New Year Chaos Call for Submission](https://github.com/CodeSpaceHQ/AppliedAlgorithms/issues/38) 28 | 4. The maintainers will now determine which solutions (there may be 0, then may be multiple) that they believe belong in this repository. If yours is picked, an issue will be created and you will be notified. 29 | - Example: [New Year Chaos Problem](https://github.com/CodeSpaceHQ/AppliedAlgorithms/issues/4) 30 | 5. If you haven't already, [Fork](https://guides.github.com/activities/forking/) this repository. 31 | 6. Create a folder for your solution in the appropriate spot (feel free to ask us if you are unsure where) and format the files in your folder appropriately. There should be 2 files (`README.md` and `solution.[EXTENSION]`) 32 | - Example: [New Year Chaos Folder](/Guide/Constructive/New%20Year%20Chaos) 33 | 7. Add your problem to the [Problem Table](/Guide/README.md) 34 | 8. Once you have written up your solution, [create a pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/) into `master`. 35 | 9. Once the PR has been created, the maintainers will go over your solution giving their thoughts & suggestions. **Do not expect that your solution and guide will be perfect on the first try.** Don't worry, the maintainers are mostly nice :) 36 | - Example: [New Year Chaos PR](https://github.com/CodeSpaceHQ/AppliedAlgorithms/pull/7) 37 | 10. Once everything is polished up and the maintainers have given their last thoughts, one of them will merge your pull request and it will be done! Your solution and guide will finally be a part of this repository. 38 | 39 | 40 | ## Pull Request Process 41 | 42 | 1. Update the appropriate documentation. 43 | 2. You may merge the Pull Request in once you have the sign-off of two other developers, or if you 44 | do not have permission to do that, you may request the second reviewer to merge it for you. 45 | 46 | ## Issue Labels 47 | If you are a contributor to this repository it is your responsibility to make sure all issues are properly labeled. 48 | 49 | We use the following, 50 | - `coding problem`: For a coding problem or solution related issue. 51 | - `discussion`: For any discussion about the project/repository. 52 | - `guide`: For assigning and discussing the guide for a certain problem. 53 | - `call for submission`: For determining whose solution goes into the repository, with a written up guide. 54 | 55 | ## Code of Conduct 56 | Don't be rude to anyone. 57 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Problem 5 | 6 | 7 | # Checklist 8 | 9 | - [ ] My solution has been tested and passes any and all test cases. 10 | - [ ] I have included a guide that explains my solution. 11 | - [ ] My solution and guide meet all requirements of [CONTRIBUTING.md](/.github/CONTRIBUTING.md) 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | idea/ 2 | -------------------------------------------------------------------------------- /Guide/Constructive/Bonetrousle/README.md: -------------------------------------------------------------------------------- 1 | # Bonetrousle 2 | [https://www.hackerrank.com/challenges/bonetrousle](https://www.hackerrank.com/challenges/bonetrousle) 3 | 4 | Category: Constructive 5 | 6 | Difficulty: Medium 7 | 8 | 9 | ## Problem 10 | 11 | ### Overview 12 | 13 | ####Given: 14 | * The number of boxes to buy, b. 15 | * The number of noodles to buy, n. 16 | * The set of boxes available to buy {1, 2, ..., k} 17 | 18 | ####Goal: 19 | * Pick a subset, S, of the boxes available to buy which: 20 | * has a size equal to b. 21 | * the values of the boxes in S equals n. 22 | 23 | ## Algorithm 24 | 25 | ### Overview 26 |

27 | For this problem you may have come up with a formula similar to e + (e + 1) + ... + (e + t - 1) = n. 28 | Where e is the number of noodles in the first box picked. However this equation by itself will not work. 29 | For example if you had the boxes 1, 2, 3, and 4 and you needed 6 noodles, no two consecutive boxes 30 | will give you six, but if you pick boxes 2 and 4 you will get 6 noodles. This algorithm modifies the 31 | formula above so that it does work. Note that the problem says that any combination of boxes that produces 32 | the needed number of noodles is a valid solution. 33 |

34 | 35 | It is important to keep in mind that for this problem some variable values could exceed the value of an int in Java. Also, `System.out.println()`, may be too slow for big inputs. Instead, use OutputStream. 36 | 37 | ### Pseudo Code 38 | 1. Read in n, k, and b. 39 | 2. Make a variable called numConstants, a variable called amountShort, and a variable called e. 40 | 3. numConstants = (b/2) * (b-1) 41 | 4. amountShort = (n - numConstants) % b 42 | 5. e = (n - numConstants) / b 43 | 6. if e < 1 or (amountShort == 0 and e+b-1 > k) or (amountShort != 0 and e+b > k): 44 | 1. print("-1") 45 | 7. else: 46 | 1. Make a variable called boxToSkip and a variable called lastNumPrinted. 47 | 2. boxToSkip = b - amountShort 48 | 3. lastNumPrinted = e 49 | 4. for i = 1 to b: 50 | 1. if i == boxToSkip: 51 | 1. lastNumPrinted = lastNumPrinted + 2 52 | 2. else: 53 | 1. lastNumPrinted = lastNumPrinted + 1 54 | 3. print(" " + lastNumPrinted) 55 | 56 | Note that the actual problem asks you to do this algorithm for t test cases. 57 | 58 | ### Analysis 59 |

60 | Since this algorithm uses mainly math, the largest part of the code is the loop that prints 61 | the list of boxes out. This loop goes b times and thusly the algorithm's time complexity is O(b). 62 |

63 | 64 | ## Conclusion 65 |

66 | As computer science is closely related to math, some problems can be easily solved 67 | using math to create a formula. This problem is a great example of this. Without the formula stated 68 | above, this problem would have been much more difficult. When starting to solve a programming 69 | problem, it is always best to see if there is a formula that can be derived quickly 70 | that will accurately produce a solution. 71 |

-------------------------------------------------------------------------------- /Guide/Constructive/Bonetrousle/solution.java: -------------------------------------------------------------------------------- 1 | import java.io.*; 2 | import java.util.Scanner; 3 | 4 | public class Bonetrousle 5 | { 6 | /* 7 | boolean boxOutOfRange(int b, long k, long e, long amountShort) 8 | This method returns True if the first box, e, is < 1 or the last box, 9 | (e+b-1) if amountShort == 0 or (e+b) otherwise, is > k and returns 10 | False otherwise. 11 | */ 12 | public static boolean boxOutOfRange(int b, long k, long e, long amountShort) 13 | { 14 | return e < 1 || ((amountShort == 0)? e+b-1 : e+b) > k; 15 | } 16 | 17 | /* 18 | void print(int b, long k, long e, long amountShort) 19 | This method prints all the boxes in the range [e, e+b] 20 | if amountShort == 0, prints all the boxes in the range 21 | [e, e+b+1] with the exception of the box (e - amountShort), 22 | or prints "-1" if there is going to be a box in the solution that 23 | is out of the range [1, k]. 24 | */ 25 | public static void print(int b, long k, long e, long amountShort) 26 | { 27 | //If the first box or last box will be out of the range [1, k] print -1. 28 | if(boxOutOfRange(b, k, e, amountShort)) 29 | { 30 | System.out.println("-1"); 31 | } 32 | //Else print all the boxes. 33 | else 34 | { 35 | int boxToSkip = (int)(b - amountShort); //The index of the box to skip in the sequence. 36 | 37 | //OutputStream is needed instead of System.out.println() becasue is is much faster. 38 | OutputStream out = new BufferedOutputStream( System.out ); 39 | long lastNumPrinted = e; 40 | 41 | out.write(("" + lastNumPrinted).getBytes()); 42 | 43 | for (int i = 1; i < b; i++) 44 | { 45 | if(i == boxToSkip) 46 | { 47 | lastNumPrinted += 2; 48 | } 49 | else 50 | { 51 | lastNumPrinted++; 52 | } 53 | 54 | out.write((" " + lastNumPrinted).getBytes()); 55 | } 56 | 57 | out.write(("\n").getBytes()); 58 | out.flush(); 59 | } 60 | } 61 | 62 | public static void main(String[] args) throws IOException 63 | { 64 | Scanner reader = new Scanner(new File("input.txt")); 65 | 66 | int trips = reader.nextInt(); //Number of times to run the algorithm. 67 | 68 | for (int i = 0; i < trips; i++) 69 | { 70 | long n = reader.nextLong(); //Number of noodles to buy. 71 | long k = reader.nextLong(); //Number of boxes for sale. 72 | int b = reader.nextInt(); //Number of boxes to buy. 73 | 74 | /* 75 | The formula for t consecutive boxes is e + (e + 1) + (e + 2) + ... + (e + t-1) = n 76 | Where e is the value of the first box. 77 | */ 78 | 79 | long numConstants = (long)((b/2.0) * (b-1)); //The number the constants in the formula add up to. 80 | 81 | long amountShort = (n - numConstants) % b; //How much the current sequence of t boxes starting 82 | //with e will be short of n. 83 | 84 | long e = (n - numConstants) / b; //The number for e in the formula. 85 | 86 | print(b, k, e, amountShort); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /Guide/Constructive/New Year Chaos/README.md: -------------------------------------------------------------------------------- 1 | # New Year Chaos 2 | 3 | 4 | 5 | 6 | ## Problem 7 | This problem comes from [HackerRank](https://www.hackerrank.com/challenges/new-year-chaos). 8 | Category: Constructive Algorithm 9 | Difficulty: Medium 10 | 11 | ### Overview 12 | Here is a breakdown of what the problem is really asking. 13 | 14 | 1. Suppose there is an ordered list of N elements numbered 1 to N. 15 | - An element can swap with the element before it at most 2 twos. 16 | - We are given a list of numbers where each number marks the original position of an element. 17 | - From this list we are to determine whether or not it was possible to get to this state given (1) & (2) 18 | - If it was not possible, output “Too chaotic”, else output the minimum number of swaps it would take to get from (1) to the given list at (3). 19 | 20 | ## Algorithm 21 | ### Overview 22 | The only data structure used in this solution is an array for storing the list. 23 | For this algorithm, we can assume that the list is possible as we are checking for that while taking in the input. 24 | If we iterate through the list backwards, swapping elements as we go we will be able to make two assumptions that are needed to solve the problem. 25 | For element with value _x_ at position _i_ , _1 <= i <=N_: 26 | 27 | Assumption 1: Since we are swapping and therefore sorting as we iterate, we can assume that for any element at the current position, all elements with positions greater than the current position are where they are supposed to be and have values greater than the value of element at the current position. 28 | 29 | Assumption 2: From the breakdown, we know that _x_ has a lower bound of i+1 and from the previous assumption we know that x has a greater bound of _i_ inclusively. Therefore, if _i_ does not equal _x_, then either _i-1_ or _i-2_ equals _x_ 30 | 31 | If (while using these assumptions, iterating through the list backwards and swapping elements as we go) we increment a counter by 1 every time _x_ is at _i-1_ and by 2 every time _x_ is at _i-2_, the counter will equal the minimum number of swaps needed by the end of the iteration. 32 | 33 | ### Pseudo Code 34 | 1. Read in values into a list _line_. 35 | - For each value _x = line[i]_ check to see if _x > i+2_. If so, return "Too chaotic" else continue 36 | - Initialize a counter to 0. 37 | 38 | We only care about 3 people & 3 positions at a time. 39 | Suppose it starts as follows: 40 | Person A at position X 41 | Person B at position Y 42 | Person C at position Z 43 | 44 | Then the line looks like this: 45 | Person : C B A 46 | Position : Z Y X 47 | 48 | We want the person at position X to be the greatest value. We can assume that anybody behind (to the right) of position X is already where they are supposed to be. AKA every person after position X has a value greater than every person at and before position X. 49 | 50 | #### Person A: 51 | First check to see if A is where they should be, if not, we need to find out if A swapped once or twice. 52 | A is not supposed to be here, since we know they aren’t behind position n, he must have been bribed! 53 | The question is, who bribed him? B or C? Regardless, we need to increment counter and find out the value of person B. 54 | 55 | If person A isn’t where he is supposed to be, then no matter whether he was bribed by B or C, A needs to be in the middle. 56 | Now where C & B need to be changes depending on whether it was C or B that did the bribing 57 | 58 | #### Person B: 59 | Now we know person A isn’t right spot, we need to see if person B is in the right spot. 60 | If we stop here, our line will look like this: CAB 61 | If person B was also not supposed to be here, C must have bribed two people! 62 | We have already incremented count once, now just increment once more to reflect that it was 2 bribes, not 1. 63 | Since we know CBA and CAB were not correct, the correct ordering of these three people to make it so the person at position X has the greatest value MUST BE BAC. 64 | Increment counter; 65 | 66 | If person B was the one who bribed person A, all we need to do is finish the swap. (Remember: We already set _line[n - 1] = x_) 67 | 68 | Finally, the counter now holds the correct output value. 69 | 70 | 71 | 72 | 73 | ### Analysis 74 | Within in the source code are two for loops (Ignoring the one for test cases). 75 | Both of these for loops are linear with size _N_. Therefore the Big-O for this algorithm is _O(N)_. 76 | Upon submitting this, the solution passes all of HackerRank’s test cases, gaining a score of 40.0. 77 | The longest test case was Test Case #6 with a runtime of 0.16s. 78 | 79 | 80 | ## Conclusion 81 | Overall this is a good problem to see as there are actually quite a few ways to solve this problem. Several of my fellow students also tried this problem and their best results had a complexity of _n*lg(n)_. This results from the back that they didn’t consider looping backwards through the list. 82 | So with that my takeaway is this; 83 | Before committing to a solution imagine your data in a different way. 84 | What changes when you sort the data first? In ascending or descending? 85 | What if the data was shuffled? 86 | Or, like in this case, what if you simply just went through the data backwards? 87 | -------------------------------------------------------------------------------- /Guide/Constructive/New Year Chaos/solution.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() { 5 | int T; // Number of test cases 1 <= T <= 10 6 | int N; // Number of people in line 1 <= N <= 10^5 7 | 8 | std::cin >> T; 9 | 10 | for (int t = 0; t < T; t++) { // For each test case 11 | std::cin >> N; // Get the number of people 12 | int n; 13 | 14 | /* 15 | So we can have a 1-based array as opposed to 0-based, makes things easier 16 | since n>=1 17 | */ 18 | int line[N + 1]; 19 | 20 | bool is_chaotic = false; //To check for chaotic while reading in numbers 21 | 22 | /* 23 | Read in the numbers and store them in the line array. 24 | This is also where we check for chaotic or not. If by the end of this 25 | loop we haven't said its chaotic, we never will. 26 | 27 | Note: n=1 Because we are working with 1-based array instead of 0 28 | */ 29 | for (n = 1; n <= N; n++) { 30 | int x; 31 | std::cin >> x; 32 | 33 | /* 34 | The reason for the flag instead of simply breaking here is because we 35 | still need to read in all the inputs. 36 | This condition is the ONLY way for the line to be chaotic. 37 | If it is chaotic we don't need to waste the time to store the values 38 | */ 39 | if (x > n + 2) is_chaotic = true; 40 | else line[n] = x; 41 | } 42 | 43 | if (is_chaotic) { 44 | /* 45 | Now that we have read all the numbers in, we can call it quits now if 46 | its chaotic 47 | */ 48 | std::cout << "Too chaotic" << std::endl; 49 | } else { 50 | int count = 0; // AKA number of bribes 51 | 52 | for (n = N; n > 0; n--) { 53 | int x = line[n]; // Person A 54 | 55 | if (x != n) { 56 | count++; // They must have bribed at least once 57 | int y = line[n - 1]; // Person B 58 | line[n - 1] = x; 59 | 60 | if (y != n) { 61 | count++; 62 | int z = line[n - 2]; // Person C 63 | line[n] = z; 64 | line[n - 2] = y; 65 | } else { 66 | line[n] = y; 67 | } 68 | } // else x == n in which case nothing needs to be done 69 | } 70 | std::cout << std::to_string(count) << std::endl; 71 | } 72 | } 73 | 74 | return 0; 75 | } 76 | -------------------------------------------------------------------------------- /Guide/Constructive/README.md: -------------------------------------------------------------------------------- 1 | # Constructive Algorithms Guide 2 | 3 | ## [Overview](https://en.wikipedia.org/wiki/Constructive_proof) 4 | Constructive Algorithms aim to show that a solution exists to a problem by providing a method that creates the desired output. 5 | 6 | ## Additional Resources 7 | 8 | Constructive Algorithms are useful when proving that something can be done, It typically requires the combination of applying mathematical principles or proofs by way of proper use of data structures and other algorithmic principles. Below are some additional resources and examples. 9 | 10 | ### Additional Explanations 11 | 12 | A more in depth explanation of what a constructive algorithm is can be found [here](http://www.cs.tau.ac.il/~nachumd/models/CASM.pdf). 13 | 14 | ### Additional Problems 15 | 16 | More Constructive Algorithm problems can be found [here](https://www.hackerrank.com/domains/algorithms/constructive-algorithms) and [here](https://www.a2oj.com/Category.jsp?ID=94). -------------------------------------------------------------------------------- /Guide/Data Structures/Contacts/README.MD: -------------------------------------------------------------------------------- 1 | # Contacts 2 | This problem comes from [HackerRank](https://www.hackerrank.com/challenges/contacts) and is under the Data Structures Domain. 3 | 4 | ##### Tags 5 | 6 | [Trie](https://en.wikipedia.org/wiki/Trie) / [Hash Table](https://en.wikipedia.org/wiki/Hash_table) / [Class](https://en.wikipedia.org/wiki/Class_(computer_programming)) 7 | 8 | ## Problem 9 | ### Statement 10 | 11 | We're going to make our own Contacts application! The application must perform two types of operations: 12 | 13 | 1. add name, where _name_ is a string denoting a contact name. This must store as a new contact in the application. 14 | 2. find partial, where _partial_ is a string denoting a partial name to search the application for. It must count the number of contacts starting with and print the count on a new line. 15 | 16 | Given _n_ sequential **_add_** and **_find_** operations, perform each operation in order. 17 | 18 | **Input Format** 19 | 20 | The first line contains a single integer, _n_, denoting the number of operations to perform. 21 | Each line _i_ of the _n_ subsequent lines contains an operation in one of the two forms defined above. 22 | 23 | Constraints 24 | * 1 <= n <= 10^5 25 | * 1 <= | _name_ | <= 21 26 | * 1 <= | _partial_ | <= 21 27 | 28 | 29 | It is guaranteed that and contain lowercase English letters only. 30 | The input doesn't have any duplicate for the operation. 31 | Output Format 32 | 33 | #### Output Format 34 | 35 | For each _find partial_ operation, print the number of contact names starting with _partial_ on a new line. 36 | 37 | #### Sample Input 38 | 4
39 | add hack
40 | add hackerrank
41 | find hac
42 | fink hak
43 | 44 | #### Sample Output 45 | 2
46 | 0
47 | 48 | #### Explanation 49 | 50 | We perform the following sequence of operations: 51 | 52 | 1. Add a contact named _hack_. 53 | 2. Add a contact named _hackerrank_. 54 | 3. Find and print the number of contact names beginning with _hac_. There are currently two contact names in the application and both of them start with _hac_, so we print 2 on a new line. 55 | 4. Find and print the number of contact names beginning with _hak_. There are two contact names in the application but neither of them start with _hak_, so we print 0 on a new line 56 | 57 | ### Overview 58 | 59 | This is a two step problem. 60 | 61 | 1. Setting up the Trie data structure. 62 | 2. Finding the number of sub strings. 63 | 64 | A Trie can be implemented in more than one way. I chose to create my own Trie class that contained a HashMap that mapped 65 | characters to other objects of type Trie. I also had an integer value in my class so I could keep track of the the times that i had 66 | seen that substring when doing the add. 67 | 68 | ## Algorithm 69 | 70 | ### Overview 71 | 72 | #### Part 1 73 | 74 | Part one of this problem lies in the creation of the data structure that will be used to store the input strings and subsequent sub-strings. 75 | This data structure is called a Trie (from re-_trie_-val) and can be thought of as a tree of characters, Like the following: 76 | 77 |      a
78 |      / \\
79 |   r     t
80 |  /
81 | t
82 | 83 | This is a trie that contains two words (art and at), as you can see it is space efficient because if they both contain the same substring (a in this case) 84 | space is not being wasted. This is the data structure we are trying to create, but at each node (or letter in this case) we would like to store an integer 85 | value of how many times we have seen this character in the past, thus our Class will need two variable. One being the HashMap that will store the Characters, and the Tries that they link to, and an integer value that will store the number of times that we have seen the substring. 86 | 87 | The next step is to add the functionality that is described in the problem. Specifically the add and find functions. The meat of this problem is in the add function. In our main function we must create an initial Trie object. The add function should take in a string and add this specific string to the Trie. in order to do this we need to separate the string out into its characters, and map each character to a trie containing the next character. The trick to get the efficience in the find functions is to keep track of the number of times we are adding each character at each specific location. Using the example from above when we add the a from art, we also need to increment counter associated with at this location. Next we must use the Trie associated with a, and add the r from art, and again increment the counter, and so forth until the whole string is added. Once we have added "art" next we want to add "at". Since we already have a in the first hash map, we dont need to add a new Trie at this location, all we need to do is 88 | increment the counter, then go to the Trie associated with a already. Next we just need to add the 't' to the new HashMap, and increment the counter and we are done. 89 | 90 | The next step is to create the find functionality. Since we have gone through the trouble of keeping track of all of the times we saw a specific character at a specific level, the find 91 | functionality is very easy to implement. At this point all we need to do is search through the trie one character at a time until we hit the end, or a character in the substring is not contained 92 | in the trie. At that point we return the integer value denoting the number of times we've seen the substring, or 0 if it is not in the trie. 93 | 94 | #### Part 2 95 | 96 | Once the class Trie class is created with all the necessary functionality, part 2 of this is super easy. All you have to do is read in the strings to add to the trie, and add them. Once you have inserted all the strings, all you need to do is search through the trie for the sub-strings and return the integer value that is associated with the last character from the substring, if it exists in the trie. 97 | 98 | 99 | ### Pseudo Code 100 | 101 | 1. Create Trie Class 102 | * HashMap that maps characters to Trie Class. 103 | * Integer value to keep track of times character has been seen. 104 | 2. Add proper functionality to Trie class. 105 | * Create Add function. 106 | * Create Find function. 107 | 3. Add all strings to trie. 108 | 4. Search through trie on find instruction and return the integer value associated with the last character. 109 | 110 | ### Analysis 111 | Adding the Strings to The trie class should take N time where N is the length of the String that you're attempting to add to the trie. 112 | Similarly the find function will take M time where M is the length of the substring. So adding is O(N) and looking for substrings is O(M) 113 | Therefor, the time complexity of this will be O(N). 114 | 115 | ### Conclusion 116 | A Trie is a very useful data structure to learn. You can clearly see the advantages of the space efficiency of this data structure. A real world example of where this data structure is used is in predictive text on your phones. For other uses feel free to browse the wikipedia page linked above. 117 | -------------------------------------------------------------------------------- /Guide/Data Structures/Contacts/Solution.java: -------------------------------------------------------------------------------- 1 | import java.io.*; 2 | import java.util.*; 3 | import java.text.*; 4 | import java.math.*; 5 | import java.util.regex.*; 6 | 7 | public class Solution { 8 | 9 | public static class TrieItUp{ 10 | 11 | HashMap mappypoo; 12 | int substrings; 13 | 14 | public TrieItUp() { 15 | mappypoo = new HashMap(); 16 | substrings = 0; 17 | } 18 | 19 | public void addsubs(){ 20 | substrings++; 21 | } 22 | 23 | public void addtomap(char key) { 24 | if(!mappypoo.containsKey(key)){ 25 | //System.err.println("Adding" + " " + key); 26 | mappypoo.put(key, new TrieItUp()); 27 | } 28 | } 29 | 30 | public TrieItUp getnext(char key) { 31 | if(mappypoo.containsKey(key)) { 32 | return mappypoo.get(key); 33 | } 34 | return null; 35 | } 36 | 37 | 38 | public int substrings() { 39 | return substrings; 40 | } 41 | 42 | public void add(String stringtoadd, TrieItUp trie) { 43 | 44 | char[] keys = stringtoadd.toCharArray(); 45 | //System.err.println(keys); 46 | for(int i = 0; i < keys.length; i++){ 47 | trie.addsubs(); 48 | trie.addtomap(keys[i]); 49 | trie = trie.getnext(keys[i]); 50 | } 51 | 52 | } 53 | 54 | public int find(String tofind, TrieItUp trie) { 55 | int subs = 0; 56 | char[] stringchar = tofind.toCharArray(); 57 | for(int i = 0; i < stringchar.length; i++) { 58 | if(trie.mappypoo.containsKey(stringchar[i])){ 59 | trie = trie.getnext(stringchar[i]); 60 | } 61 | else { 62 | return 0; 63 | } 64 | subs = trie.substrings(); 65 | } 66 | return subs; 67 | } 68 | } 69 | 70 | public static void main(String[] args) { 71 | Scanner scan = new Scanner(System.in); 72 | int cases = scan.nextInt(); 73 | scan.nextLine(); 74 | TrieItUp contacts = new TrieItUp(); 75 | for(int i = 0; i < cases; i++) { 76 | String fullcommand = scan.nextLine(); 77 | String[] command = fullcommand.split("\\s"); 78 | if(command[0].equals("find")) { 79 | System.out.println(contacts.find(command[1], contacts)); 80 | } 81 | if(command[0].equals("add")) { 82 | contacts.add(command[1], contacts); 83 | } 84 | //System.err.println(contacts.maplength()); 85 | 86 | 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /Guide/Divide and Conquer/Educational/Counting Inversions/README.md: -------------------------------------------------------------------------------- 1 | # Counting Inversions 2 | 3 | This problem is discussed in [Dr. Gelfond Applied Algorithms](http://redwood.cs.ttu.edu/~mgelfond/FALL-2012/slides.pdf) and in [Geeks for Geeks](http://www.geeksforgeeks.org/counting-inversions/) 4 | 5 | 6 | Category: 7 | 8 | Difficulty to Understand: 9 | 10 | ## Problem 11 | Given a person’s preferences (for books, movies, etc.) 12 | match them with preferences of other people on the Web with 13 | similar interests to provide a suggestion. 14 | 15 | Preferences are often defined by rankings, labeling the objects 16 | from 1 to n. So the problem is to define the distance between 17 | two people's rankings. Calculate the number of inversions between each person's 18 | rankings to represent the distance between them. 19 | 20 | ### Overview 21 | Finding "similarity" or "distance" between two rankings. Given a sequence of n numbers 1..n (assume all numbers are distinct). Define a measure that tells us how far this list is from being in ascending order. 22 | The value should be 0 if a_1 < a_2 < ... < a_n and should be higher as the list is more "out of order". 23 | 24 | *Inversion*: _i_, _j_ form an inversion if ai > aj, i.e. if the two elements ai and aj are out of order. 25 | 26 | We simply need to sort the list of preferences in ascending order and count the number of swaps we have to do. 27 | 28 | ### Input Format 29 | A list of rankings [0, 1, 2, 3, 4, ...n] 30 | 31 | ### Constraints 32 | Every number in the input list must be unique, a person can not give two items equal rankings. 33 | 34 | ### Output Format 35 | - The original set 36 | - The resulting count of inversions 37 | - The newly sorted set 38 | 39 | ## Algorithm 40 | ### Overview 41 | We will use a [Divide and Conquer](https://en.wikipedia.org/wiki/Divide_and_conquer_algorithm) algorithm. This type of algorithm is used in popular sorting algorithms like quicksort and merge sort, as well as various other algorithms. 42 | The algorithm consists of two functions: 43 | 1. count_inversions: separate the original list into sub lists and, 44 | 2. merge: sort and count inversions in each sub list while combining them in sorted order. 45 | 46 | Count Inversions and Merge are almost identical to [merge sort](https://en.wikipedia.org/wiki/Merge_sort#Analysis), except for the extra part where we keep track of inversions we find. 47 | 48 | During the `Merge` function, whenever we find an element Xi such that Xi > Yj but _i_ < _j_, we will increase the inversion count by the number of elements in the list that Xi is in. 49 | i.e. `merge([3,4], [2])` would return ``(2, [2, 3, 4])`` because _2_ < _3_ and the size of ``[3, 4]`` is 2. 50 | 51 | Below is a graphical model of a merge sort on a given sequence, this is exactly what our algorithm will do, as well as keep track of the inversions in the original list. 52 | The separations of the blocks are happening in the recursive calls of `count_inversions`, and the combining of them are happening in the recursive calls of `merge`, 53 | 54 | ![Merge Sort](assets/merge_sort_pic.png "Walk through picture of merge sort") 55 | 56 | 57 | ### Pseudo Code 58 | 59 | ```Python 60 | 61 | def count_inversions(list s of preferences): 62 | """ 63 | :returns: the number of inversions in s as well as sorted s 64 | """ 65 | if the size of s is 1 then 66 | return 0 as the count of inversions and s as the set 67 | Divide s into two halves, A and B 68 | count_A, A = count_inversions(A) # recursive call the first half 69 | count_B, B = count_inversions(B) # recursive call the second half 70 | result, set = merge(A, B) # count/merge the two halves together 71 | return count_A + count_B + result, S 72 | 73 | def merge(list A, list B): 74 | """ 75 | :returns: the number of pairs (X, Y) such that X is in A and 76 | Y is in B and X > Y, with the sorted list C containing 77 | elements of A and B. 78 | """ 79 | int i, j = 0 80 | int count 81 | list C 82 | while both A and B are not empty: 83 | move the smaller of a[i] and b[j] to C 84 | if b[j] was smaller, then increase count by the size of A 85 | advance the pointer (i or j) of the smaller number's list 86 | Put the remainder of the non-empty list in C 87 | return count, C 88 | 89 | ``` 90 | 91 | 92 | ### Analysis 93 | Since the algorithm is only slightly different from the merge sort algorithm, it inherits the 94 | merge sort time complexity which is: 95 | 96 | - worst case: O(n log n) 97 | - avg case: O(n log n) 98 | 99 | ## Example 100 | 101 | ![Counting Inversions](assets/ci_example.png "Walk through picture of counting inversions") 102 | 103 | ---- 104 | 105 | ## Conclusion 106 | The number of inversions for two people who's preferences lists or 'rankings' get passed into our counting inversions 107 | algorithm can be compared to see how 'far apart' their preferences are. If not very far apart, we could assume that the two 108 | have a similar taste in the subject of which they were ranking. 109 | 110 | Counting inversions by using merge sort by default sorts the input array. If we only wanted 111 | to count inversions but keep the original array unsorted, we would need to create a copy of the original array and 112 | call `counting_inversions` on it. 113 | -------------------------------------------------------------------------------- /Guide/Divide and Conquer/Educational/Counting Inversions/assets/ci_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Divide and Conquer/Educational/Counting Inversions/assets/ci_example.png -------------------------------------------------------------------------------- /Guide/Divide and Conquer/Educational/Counting Inversions/assets/merge_sort_pic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Divide and Conquer/Educational/Counting Inversions/assets/merge_sort_pic.png -------------------------------------------------------------------------------- /Guide/Divide and Conquer/Educational/Counting Inversions/solution.py: -------------------------------------------------------------------------------- 1 | def count_inversions(preference_set): 2 | """ 3 | Split the preference set up and recursively sort and count each one 4 | using the merge function 5 | :param preference_set: the set of preferences to sort and count 6 | :return: the number of found inversions and the sorted set 7 | """ 8 | 9 | if len(preference_set) < 2: # no inversions if size not => 2 10 | return 0, preference_set 11 | 12 | halfway = len(preference_set) // 2 13 | first_half = preference_set[:halfway] # first half of the set 14 | second_half = preference_set[halfway:] # second half of the set 15 | 16 | inv_a, a = count_inversions(first_half) 17 | inv_b, b = count_inversions(second_half) 18 | inv_c, solution = merge(a, b) 19 | 20 | return inv_a + inv_b + inv_c, solution 21 | 22 | 23 | def merge(a, b): 24 | """ 25 | Merge two lists together and count any inversions during the process 26 | :param a: the first sequence to be merged 27 | :param b: the second sequence to be merged 28 | :return: the result of merging a and b above, and the count of inversions 29 | discovered while merging them 30 | """ 31 | solution = [] # the sorted solution list 32 | inversion_count = 0 33 | while a and b: 34 | num_a = a[0] 35 | num_b = b[0] 36 | if num_a > num_b: 37 | solution.append(num_b) # put the smaller number in the solution 38 | b.remove(num_b) # remove the number from the original set 39 | inversion_count += len(a) # increase the inversion count 40 | else: 41 | solution.append(num_a) # put the smaller number in the solution 42 | a.remove(num_a) # remove the number from the original set 43 | 44 | # append the remaining elements in the non-empty list to the solution 45 | if a: 46 | solution += a 47 | else: 48 | solution += b 49 | 50 | return inversion_count, solution 51 | 52 | 53 | def main(): 54 | 55 | my_prefs = [38, 27, 43, 3, 9, 82, 10] # create a preference list 56 | inversion_count, sorted_set = count_inversions(my_prefs) 57 | print("Original Set: {}\nInversions: {}\nSorted Set: {}" 58 | .format(my_prefs, inversion_count, sorted_set)) 59 | 60 | if __name__ == '__main__': 61 | main() -------------------------------------------------------------------------------- /Guide/Divide and Conquer/Practice/README.md: -------------------------------------------------------------------------------- 1 | HackerRank and other problems which require divide and conquer algorithms go here. -------------------------------------------------------------------------------- /Guide/Divide and Conquer/README.md: -------------------------------------------------------------------------------- 1 | # Divide and Conquer Algorithms Guide 2 | 3 | ## [Overview](https://en.wikipedia.org/wiki/Divide_and_conquer_algorithm) 4 | Divide and Conquer Algorithms break given input into parts, solve each part recursively, and then combines the solution of these sub-problems into an 5 | overall solution. 6 | 7 | 8 | ## Additional Resources 9 | 10 | This divide and conquer technique is the basis of efficient algorithms for all kinds of problems, such as sorting (e.g., quicksort, merge sort), multiplying large numbers (e.g. the Karatsuba algorithm), finding the closest pair of points, syntactic analysis (e.g., top-down parsers), and computing the discrete Fourier transform (FFTs). 11 | 12 | ### Additional Examples 13 | 14 | Detailed explinations covering divide and conquer algorithms can be found [here](http://www.geeksforgeeks.org/divide-and-conquer-introduction/) 15 | 16 | -------------------------------------------------------------------------------- /Guide/Dynamic Programming/Educational/Segmented Least Squares/README.md: -------------------------------------------------------------------------------- 1 | # Segmented Least Squares 2 | 3 | This problem is introduced in [Dr. Gelfond Applied Algorithms](http://redwood.cs.ttu.edu/~mgelfond/FALL-2012/slides.pdf) and is further discussed by [UMASS](https://people.cs.umass.edu/~sheldon/teaching/mhc/cs312/2013sp/Slides/Slides15%20-%20Segmented%20Least%20Squares.pdf) as well as other universities. 4 | 5 | Category: Dynamic Programming 6 | 7 | Difficulty: Hard 8 | 9 | ## Problem 10 | 11 | Given _n_ points in the plane: _(x1, y1), (x2, y2),..., (xn, yn)_. 12 | Find **a sequence of lines** that minimizes _f(x) = E + cL_ where: 13 | - _E_ is the sum of the sum of squared errors for each segment 14 | - _L_ is the number of segments in the solution 15 | - _c_ > 0 is a given constant 16 | 17 | ### Overview 18 | 19 | To understand the problem of _Segmented least squares_ we must first understand the problem of _Least Squares_. 20 | The notes over these two problems are taken from Princeton's lecture slides on them found above. 21 | 22 | #### Least Squares 23 | 24 | Given _n_ points in the plane: _(x1, y1), (x2, y2),..., (xn, yn)_. 25 | Find **a line** _y = ax + b_ that minimizes the sum of the squared error given by the equation: 26 | 27 | ![Sum of squared errors formula](./assets/sse.png) 28 | 29 | The SSE formula is minimized when the slope _a_ of the line and the y-intercept _b_ are found using: 30 | 31 | ![slope and intercept formulas for minimizing sum of the squared errors](./assets/a_and_b.png) 32 | 33 | A Least Squares solution would look like this: 34 | 35 | ![Least squares solution example](./assets/least_squares_sol.png) 36 | 37 | The red line from the point to the solution line is the error value for that point. The solution line minimizes the sum of all 38 | of the error values for each point. 39 | 40 | #### Segmented Least Squares 41 | 42 | Given _n_ points in the plane: _(x1, y1), (x2, y2),..., (xn, yn)_. 43 | Find **a sequence of lines** that minimizes _f(x) = E + cL_ where: 44 | - _E_ is the sum of the sum of squared errors for each segment 45 | - _L_ is the number of segments in the solution 46 | - _c_ > 0 is a given constant 47 | 48 | The difference is is that here we are trying to find multiple "lines" or "segments" that 49 | will minimize the sum of squared errors while also trying to balance how many segments we have (using the given _c_). 50 | 51 | A Segmented least squares solution could look like this: 52 | 53 | ![Segmented least squares solution 1](./assets/seg_least_squares_sol1.png) 54 | 55 | Another solution if the given _c_ value was really low could be: 56 | 57 | ![Segmented least squares solution 2](./assets/seg_least_squares_sol2.png) 58 | 59 | As you can see, _c_ will control how many lines v.s. how accurate the fit is. 60 | 61 | ### Input Format 62 | 63 | 1. A list of points in _S_ where each point is in the format `[x, y]`. An example input would look like: 64 | ```Python 65 | [[10, 9], [3, 4], [5, 6]] 66 | ``` 67 | 2. A constant _c_ > 0 which is the cost of adding a new segment to the optimal solution. 68 | 69 | ### Output Format 70 | 71 | - the total "cost" of the optimal solution (i.e. the sum of the costs of the segments), and 72 | - a visual graph depicting the points in _S_ and the segments in the optimal solution. 73 | 74 | ## Algorithm 75 | ### Overview 76 | - _OPT(j)_ will denote the minimum cost for a segment through the points _pi,pi+1,...,pj_. 77 | - _e(i,j)_ will denote the minimum sum of squared errors for points _pi,pi+1,...,pj_ 78 | 79 | To compute _OPT(j)_ we will use the formula: 80 | 81 | ![OPT(j)](./assets/opt_j.png) 82 | 83 | Where: 84 | - _OPT(i)_ is the cost of the previous optimal segment through the points _pi`,pi+1,...,pi_, 85 | - _c_ is the cost of adding a new segment and, 86 | - _e(i,j)_ is the sum of squared errors for a segment running though points _pi,...,pj_ 87 | 88 | Our algorithm is as follows: 89 | 90 | 1. Sort _S_ by x coordinates. 91 | 2. Compute _e(i,j)_ for every possible segment in _S_. 92 | 3. For each point _j_ in _S_, find _OPT(j)_, storing the value in array _m_. 93 | 4. Since _m_ keeps track of the accumulating costs of the optimal segments in the solution, the cost of the optimal solution will be in the last element of _m_. 94 | 95 | It should be noted that in our solution file we include some variables for keeping track of the points that exist in the optimal solution for the purpose 96 | of plotting them visually later. 97 | 98 | ### Pseudo Code 99 | 100 | This pseudo code follows the above algorithm and returns the cost of the optimal solution. As stated previously, in the 101 | actual implementation there are a few more steps so that the optimal solution can be graphed. 102 | 103 | ````Python 104 | def segmented_least_squares(points, c): 105 | sort(points) # sort by x values 106 | n = len(points) 107 | 108 | # Compute Sum of Squared Error for each segment 109 | for j = 1 to n: 110 | for i = 0 to j: 111 | compute the least squares error e(i,j) for the segment pi, pi+1,...,pj 112 | 113 | # Compute cost of optimal segment 114 | m[0] = 0 # initialize array with 0 cost 115 | for j = 1 to n: 116 | for i = 0 to j: 117 | m[j] = min(e(i,j) + c + m[i]) # m[j] is the minimum costing segment for the points pi to pj 118 | 119 | return m[n-1] 120 | ```` 121 | ## Analysis 122 | 123 | The segmented least squares algorithm has a time complexity of O(n3): 124 | - O(n3) for finding all _e(i,j)_ values + O(n2) for finding all _OPT(j)_ values. 125 | 126 | and a space complexity of O(n2). 127 | 128 | This time complexity can be improved to O(n2) time and O(n) space by pre-computing 129 | various statistics. 130 | 131 | 132 | ## Example 133 | 134 | ![Initial Graph](./assets/initial_plot.png) 135 | 136 | Our example points will be 137 | ``` 138 | [1, 1], 139 | [2, 3], 140 | [4, 4], 141 | [5, 6], 142 | [7, 9] 143 | ``` 144 | and our `c` value will be `c = 0.5` 145 | 146 | The first step in our algorithm is to find _eij_ for each segment _pi,...pj_ in _S_ 147 | 148 | Recall that to do this we will have to calculate: 149 | 150 | ![Sum of Squared Error Line](./assets/sse.png) 151 | 152 | where 153 | 154 | ![Sum of Squared Error Line](./assets/a_and_b.png) 155 | 156 | 157 | ![Segment 0...1](./assets/seg_01.png) 158 | 159 | For the segment p0,...p1, _e01_ = 0.0 160 | 161 | ![Segment 0...2](./assets/seg_02.png) 162 | 163 | For the segment p0,...p2, _e02_ = 0.643 164 | 165 | ![Segment 1...2](./assets/seg_12.png) 166 | 167 | For the segment p1,...p2, _e12_ = 0.0 168 | 169 | ![Segment 0...3](./assets/seg_03.png) 170 | 171 | For the segment p0,...p3, _e03_ = 0.9 172 | 173 | ![Segment 1...3](./assets/seg_13.png) 174 | 175 | For the segment p1,...p3, _e13_ = 0.643 176 | 177 | ![Segment 2...3](./assets/seg_23.png) 178 | 179 | For the segment p2,...p3, _e23_ = 0.0 180 | 181 | ![Segment 0...4](./assets/seg_04.png) 182 | 183 | For the segment p0,...p4, _e04_ = 1.3246 184 | 185 | ![Segment 1...4](./assets/seg_14.png) 186 | 187 | For the segment p1,...p4, _e14_ = 1.3077 188 | 189 | ![Segment 2...4](./assets/seg_24.png) 190 | 191 | For the segment p2,...p4, _e24_ = 0.071 192 | 193 | ![Segment 3...4](./assets/seg_34.png) 194 | 195 | For the segment p3,...p4, _e34_ = 0.0 196 | 197 | 198 | Next we will compute the optimal solution using _OPT(j)_. We will initialize 199 | our optimal cost array with _m[0] = 0_. 200 | 201 | Calculate least costing segments through _p0...p1_: 202 | 203 | - _m_ = [0] 204 | - _m[1] = OPT(1) = min0<=i<1(e(i, j) + 0.5 + OPT(i))_ 205 | - `i = 0, j = 1`, cost = 0.0 + 0.5 + 0 = 0.5 206 | - _m[1]_ = min(0.5) = 0.5 207 | 208 | Calculate least costing segments through _p0...p2_: 209 | 210 | - _m_ = [0, 0.5] 211 | - _m[2] = OPT(2) = min0<=i<2(e(i, j) + 0.5 + OPT(i))_ 212 | - `i = 0, j = 2`, cost = 0.643 + 0.5 + 0 = 1.143 213 | - `i = 1, j = 2`, cost = 0.0 + 0.5 + 0.5 = 1 214 | - _m[2]_ = min(1, 1.143) = 1 215 | 216 | Calculate least costing segments through _p0...p3_: 217 | 218 | - _m_ = [0, 0.5, 1] 219 | - _m[3] = OPT(3) = min0<=i<3(e(i, j) + 0.5 + OPT(i))_ 220 | - `i = 0, j = 3`, cost = 0.9 + 0.5 + 0 = 1.4 221 | - `i = 1, j = 3`, cost = 0.643 + 0.5 + 0.5 = 1.643 222 | - `i = 2, j = 3`, cost = 0.0 + 0.5 + 1 = 1.5 223 | - _m[3]_ = min(1.4, 1.643, 1.5) = 1.4 224 | 225 | Calculate least costing segments through _p0...p4_: 226 | 227 | - _m_ = [0, 0.5, 1, 1.4] 228 | - _m[4] = OPT(4) = min0<=i<4(e(i, j) + 0.5 + OPT(i))_ 229 | - `i = 0, j = 4`, cost = 1.3246 + 0.5 + 0 = 1.8246 230 | - `i = 1, j = 4`, cost = 1.3077 + 0.5 + 0.5 = 2.3077 231 | - `i = 2, j = 4`, cost = 0.071 + 0.5 + 1 = 1.571 232 | - `i = 3, j = 4`, cost = 0.0 + 0.5 + 1.4 = 1.9 233 | - _m[4]_ = min(1.8246, 2.3077, 1.571, 1.9) = 1.571 234 | 235 | 236 | Our final array _m = [0, 0.5, 1, 1.4, 1.571] denotes the optimal solution has a cost of _m[n-1]_ = 1.571. 237 | 238 | If we trace back through our computations for the optimal solution and keep track of the points included in 239 | calculating the minimum values, we can then plot the segments included in the solution. This is what is done 240 | in the `solution.py` file, and the final solution looks like: 241 | 242 | ![Solution](./assets/solution.png) 243 | 244 | ## Conclusion 245 | 246 | The final solution can be changed by changing the value of the input `c`, and thereby adjusting how much it 247 | "costs" to add a new segment. 248 | 249 | -------------------------------------------------------------------------------- /Guide/Dynamic Programming/Educational/Segmented Least Squares/assets/a_and_b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Dynamic Programming/Educational/Segmented Least Squares/assets/a_and_b.png -------------------------------------------------------------------------------- /Guide/Dynamic Programming/Educational/Segmented Least Squares/assets/initial_plot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Dynamic Programming/Educational/Segmented Least Squares/assets/initial_plot.png -------------------------------------------------------------------------------- /Guide/Dynamic Programming/Educational/Segmented Least Squares/assets/least_squares_sol.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Dynamic Programming/Educational/Segmented Least Squares/assets/least_squares_sol.png -------------------------------------------------------------------------------- /Guide/Dynamic Programming/Educational/Segmented Least Squares/assets/opt_j.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Dynamic Programming/Educational/Segmented Least Squares/assets/opt_j.png -------------------------------------------------------------------------------- /Guide/Dynamic Programming/Educational/Segmented Least Squares/assets/seg_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Dynamic Programming/Educational/Segmented Least Squares/assets/seg_01.png -------------------------------------------------------------------------------- /Guide/Dynamic Programming/Educational/Segmented Least Squares/assets/seg_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Dynamic Programming/Educational/Segmented Least Squares/assets/seg_02.png -------------------------------------------------------------------------------- /Guide/Dynamic Programming/Educational/Segmented Least Squares/assets/seg_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Dynamic Programming/Educational/Segmented Least Squares/assets/seg_03.png -------------------------------------------------------------------------------- /Guide/Dynamic Programming/Educational/Segmented Least Squares/assets/seg_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Dynamic Programming/Educational/Segmented Least Squares/assets/seg_04.png -------------------------------------------------------------------------------- /Guide/Dynamic Programming/Educational/Segmented Least Squares/assets/seg_12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Dynamic Programming/Educational/Segmented Least Squares/assets/seg_12.png -------------------------------------------------------------------------------- /Guide/Dynamic Programming/Educational/Segmented Least Squares/assets/seg_13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Dynamic Programming/Educational/Segmented Least Squares/assets/seg_13.png -------------------------------------------------------------------------------- /Guide/Dynamic Programming/Educational/Segmented Least Squares/assets/seg_14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Dynamic Programming/Educational/Segmented Least Squares/assets/seg_14.png -------------------------------------------------------------------------------- /Guide/Dynamic Programming/Educational/Segmented Least Squares/assets/seg_23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Dynamic Programming/Educational/Segmented Least Squares/assets/seg_23.png -------------------------------------------------------------------------------- /Guide/Dynamic Programming/Educational/Segmented Least Squares/assets/seg_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Dynamic Programming/Educational/Segmented Least Squares/assets/seg_24.png -------------------------------------------------------------------------------- /Guide/Dynamic Programming/Educational/Segmented Least Squares/assets/seg_34.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Dynamic Programming/Educational/Segmented Least Squares/assets/seg_34.png -------------------------------------------------------------------------------- /Guide/Dynamic Programming/Educational/Segmented Least Squares/assets/seg_least_squares_sol1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Dynamic Programming/Educational/Segmented Least Squares/assets/seg_least_squares_sol1.png -------------------------------------------------------------------------------- /Guide/Dynamic Programming/Educational/Segmented Least Squares/assets/seg_least_squares_sol2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Dynamic Programming/Educational/Segmented Least Squares/assets/seg_least_squares_sol2.png -------------------------------------------------------------------------------- /Guide/Dynamic Programming/Educational/Segmented Least Squares/assets/solution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Dynamic Programming/Educational/Segmented Least Squares/assets/solution.png -------------------------------------------------------------------------------- /Guide/Dynamic Programming/Educational/Segmented Least Squares/assets/sse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Dynamic Programming/Educational/Segmented Least Squares/assets/sse.png -------------------------------------------------------------------------------- /Guide/Dynamic Programming/Educational/Segmented Least Squares/solution.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plot 2 | 3 | 4 | def compute_sse(points): 5 | """ 6 | Compute the minimum sum of the squared error of 7 | a line through given points. 8 | :param points: a list of points [[x,y],...] 9 | :return: the minimum sum of squared error value for the 10 | line passing through the points 11 | """ 12 | n = len(points) 13 | # all x values for segment 14 | x_cords = [p[0] for p in points] 15 | # all y values for segment 16 | y_cords = [p[1] for p in points] 17 | # xi*yi values for segment 18 | xy = map(lambda x, y: x * y, x_cords, y_cords) 19 | # sum of all x values for segment 20 | sum_x = sum(x_cords) 21 | # sum of all y values for segment 22 | sum_y = sum(y_cords) 23 | a_numerator = n * (sum(xy)) - (sum_x * sum_y) 24 | a_denominator = n * (sum([x ** 2 for x in x_cords])) - sum_x ** 2 25 | # slope of line 26 | a = a_numerator / a_denominator if a_denominator > 0 else float('inf') 27 | # y-intercept of line 28 | b = (sum_y - (a * sum_x)) / n # y-intercept 29 | # Sum of squared errors of line with slope a y-intercept b going through 30 | # points (xi, yi) 31 | err = sum( map(lambda x, y: (y - a * x - b) ** 2, x_cords, y_cords)) 32 | return err 33 | 34 | 35 | def segment_least_squares(points, c): 36 | """ 37 | Given points in a plane and a constant cost > 0, find a squesnce of lines 38 | that minimizes f(x) = E + cost*L where: 39 | E is the sum of the sums of the squared errors in each segment 40 | L is the number of lines 41 | :param points: list of points where each point is in the format [x, y] 42 | :param c: the cost of creating a new segment 43 | :return: 44 | A list of accumulated cost values for the optimal solution's 45 | segments, and a list containing the starting points of each segment 46 | as its values, with the index of that list being the end points. 47 | 48 | """ 49 | n = len(points) 50 | # Sort point by x-coordinate 51 | points.sort(key=lambda k: k[0]) 52 | # Initialize matrix to hold SSE error values 53 | e = [[0 for x in range(n)] for y in range(n)] 54 | 55 | for j in range(1, n): 56 | for i in range(0, j): 57 | # Get all points to try segments on 58 | included_points = points[i:j + 1] 59 | # Compute SSE eij for this segment 60 | error = compute_sse(included_points) 61 | e[i][j] = error 62 | print(e) 63 | # List to track accumulating cost 64 | m = [0] * n 65 | # List to track start/end points of segments 66 | segments = [0] * n 67 | for j in range(1, n): 68 | # Initial minimum cost 69 | min_cost = float('inf') 70 | for i in range(0, j): 71 | # compute cost for segment from point i to j 72 | cost = e[i][j] + c + m[i] 73 | if cost < min_cost: 74 | # Save new minimum cost 75 | min_cost = cost 76 | # Save new minimum segment 77 | segments[j] = i 78 | m[j] = min_cost 79 | print(m) 80 | return m[-1], segments 81 | 82 | 83 | def find_segments(points, seg_starts): 84 | """ 85 | Find each point included in either end of a segment 86 | :param points: list of points [[x,y], ...] 87 | :param seg_starts: list of starting points for segments. seg_starts[i] is 88 | the starting point of a segment while i is the end point 89 | :return: each point in points included in the endpoints of all the segments 90 | """ 91 | if len(points) == 1: 92 | # exclude last point, it will always point to itself 93 | return points 94 | else: 95 | included_point = points[-1] 96 | new_cutoff = seg_starts[-1] + 1 97 | return find_segments(points[:new_cutoff], 98 | seg_starts[:new_cutoff]) + [included_point] 99 | 100 | 101 | def plot_all(points, segment_points): 102 | """ 103 | Plot all points and segments in the optimal solution using matplotlib 104 | :param points: list of points [x,y] 105 | :param segment_points: list of segment endpoints points [x,y] in points 106 | :return: show a graph with all points and segments plotted. 107 | """ 108 | xs = [p[0] for p in points] 109 | ys = [p[1] for p in points] 110 | plot.plot(xs, ys, 'ro') 111 | 112 | xs = [p[0] for p in segment_points] 113 | ys = [p[1] for p in segment_points] 114 | plot.plot(xs, ys) 115 | 116 | plot.show() 117 | 118 | 119 | def main(): 120 | points = [ 121 | [1, 1], 122 | [2, 3], 123 | [4, 4], 124 | [5, 6], 125 | [7, 9] 126 | ] 127 | 128 | cost, segments = segment_least_squares(points, .5) 129 | print("Total cost of line(s) through points: {}".format(cost)) 130 | endpoints = find_segments(points, segments) 131 | plot_all(points, endpoints) 132 | 133 | 134 | if __name__ == '__main__': 135 | main() 136 | -------------------------------------------------------------------------------- /Guide/Dynamic Programming/Educational/Weighted Interval Scheduling/README.md: -------------------------------------------------------------------------------- 1 | # Shortest Path Tree in a Graph 2 | 3 | This problem is introduced in [Dr. Gelfond Applied Algorithms](http://redwood.cs.ttu.edu/~mgelfond/FALL-2012/slides.pdf) and is further discussed by [Washington University](https://courses.cs.washington.edu/courses/cse521/13wi/slides/06dp-sched.pdf) 4 | 5 | This problem is a variation of the simpler [Interval Scheduling](https://github.com/CodeSpaceHQ/AppliedAlgorithms/tree/master/Guide/Greedy/Educational/Interval%20Scheduling) problem that can be solved using a greedy algorithm. 6 | 7 | Category: Dynamic Programming 8 | 9 | Difficulty: Medium 10 | 11 | ## Problem 12 | Given _n_ requests where each request _i_ has an associated interval _[si, fi)_ and has a weight/value 13 | _vi_, and one server, select a subset of mutually compatible requests with maximum weight/value. 14 | 15 | ### Overview 16 | Ordering intervals by the earliest finishing time like we did in the [Interval Scheduling](https://github.com/CodeSpaceHQ/AppliedAlgorithms/tree/master/Guide/Greedy/Educational/Interval%20Scheduling) solution does not work. 17 | Later intervals may not be compatible with the current one and may have much higher weights. 18 | 19 | First we must solve a simpler problem - finding the maximum sum of weights. To do this we will use a helper function `opt()` that will return 20 | an array containing the max value for a schedule containing j and not containing j for each request j. 21 | 22 | ### Input Format 23 | 24 | A list of requests R such that each request _i_ has a start time _si_, a finish time _fi_ a value or weight _vi_ and an index 25 | of the first previous request compatible with _i_. 26 | 27 | ### Constraints 28 | The input list of requests must be sorted by finish time before the main part of our algorithm. 29 | 30 | ### Output Format 31 | The output will be a list of indices of the requests in the optimal schedule. If the sorted list of request is [A, C, B, E, D, G, F] and the optimal 32 | schedule is [C, D, F], our algorithm will return [1, 4, 6]. 33 | 34 | ## Algorithm 35 | ### Overview 36 | Finding an optimal schedule of weighted intervals has a few steps: 37 | 1. Sort the requests by finish time 38 | 2. Find _p(j)_ for each request _j_ where _p(j)_ is a request _i < j_ that is compatible with _j_. 39 | 3. Find the value of optimal schedules containing _j_ and not containing _j_ for every request _j_. 40 | 4. Using the list from step #3, find the schedule with the max sum of request values. 41 | 42 | So the trickiest part here is step #3. Basically to accomplish this we loop through each request _r_ in the set of requests _R_ and see if _r_ plus its previous 43 | compatible request values is bigger than the request before it plus its previous compatible request values. The easiest way at this point to understand this is just by seeing 44 | its implementation and pseudo code. 45 | 46 | 47 | 48 | ### Pseudo Code 49 | 50 | ```Python 51 | 52 | def opt(requests: set of requests scheduled by finish time): 53 | m = [] 54 | m[0] = 0 # the first request has no previous requests.. so its opt is 0 55 | for i from 1...len(requests): 56 | # m[i] is the max between this request and its previous compatible 57 | # requests, and the previous set of compatible requests. 58 | m[i] = max(value of i + m[p(i)], m[i-1]) 59 | return m 60 | 61 | def find_solution(m, i): 62 | # i is the last index in the array of requests 63 | if i == 0: 64 | return [i] 65 | if value of request at index i + m[p(i)] > m[i-1]: 66 | return find_solution(m, p(i)) + [i] 67 | return find_solution(m, i-1) 68 | 69 | ``` 70 | 71 | ## Analysis 72 | The time complexity of the algorithm in is O(n). Sorting the requests before finding the optimal solution is O(n log n), but each step after that (finding previous indices, finding the optimal values, and finding the requests) are O(n). 73 | 74 | ## Example 75 | ![Initial Requests](./assets/initial1.png) 76 | 77 | We start with 9 unsorted requests. To find an optimal schedule we will need to first 78 | sort them by finish time. 79 | 80 | ![Sorted Requests](./assets/sorted1.png) 81 | 82 | Now that they are sorted we can start parts of our algorithm. We need to find out _p(j)_ for every request _j_. 83 | Remember that _p(j)_ is the request _i < j_ that is compatible with _j_. 84 | 85 | Here is a table detailing each request _j_ and _p(j)_: 86 | 87 | | Request | j | v(j) | p(j) | 88 | | ------------- |----| ----- |----- | 89 | | (0, 10, 2) | 1 | 10 | 0 | 90 | | (1, 5, 4) | 2 | 5 | 0 | 91 | | (0, 10, 5) | 3 | 10 | 0 | 92 | | (2, 8, 7) | 4 | 8 | 1 | 93 | | (3, 2, 8) | 5 | 2 | 1 | 94 | | (5, 3, 10) | 6 | 3 | 3 | 95 | | (7, 7, 11) | 7 | 7 | 4 | 96 | | (11, 4, 15) | 8 | 4 | 7 | 97 | | (12, 11, 17) | 9 | 11 | 7 | 98 | 99 | I know I know, arrays start at 0. A _j_ value of 1 is simply representing the first request. This will help us later in the algorithm. 100 | 101 | So the first three requests in our sorted requests do not have any previously compatible requests, and thus have _p(j)_ = 0. The request `(2, 8, 7)` is compatible with request 1, so it's _p(j)_ = 1, 102 | and so on. 103 | 104 | Next we need to determine the maximum values for a schedule that we can get by following each request through their subsequent compatible requests. 105 | 106 | | Request | j | v(j) | p(j) | m[j] | 107 | | ------------- |----| ----- |----- | ------ | 108 | | | 0 | 0 | - | 0 | 109 | | (0, 10, 2) | 1 | 10 | 0 | 10 | 110 | | (1, 5, 4) | 2 | 5 | 0 | | 111 | | (0, 10, 5) | 3 | 10 | 0 | | 112 | | (2, 8, 7) | 4 | 8 | 1 | | 113 | | (3, 2, 8) | 5 | 2 | 1 | | 114 | | (5, 3, 10) | 6 | 3 | 3 | | 115 | | (7, 7, 11) | 7 | 7 | 4 | | 116 | | (11, 4, 15) | 8 | 4 | 7 | | 117 | | (12, 11, 17) | 9 | 11 | 7 | | 118 | 119 | _m[j]_ will represent the maximum value of a scheduling solution for the items 1...j. This is found by using the relation: 120 | ``` 121 | m[j] = max(v(j) + m[p(j)], m[j-1]) 122 | ``` 123 | 124 | The first index of _m[]_ is initialized with the value 0. This allows us to say that the first request (at index 1) has no previous scheduling solution/value (a schedule has not been made). 125 | 126 | So, _m[1_] will be the maximum of 10 + 0, and 0. (pssst.. its 10). Lets continue filling out _m[]_. 127 | 128 | _m[2]_ for request j = 2 will still be 10, because its evaluation was `m[j] = max(v(j) + m[p(j)], m[j-1])` = `m[j] = max(5 + 0, 10)`. 129 | 130 | 131 | | Request | j | v(j) | p(j) | m[j] | 132 | | ------------- |----| ----- |----- | ------ | 133 | | | 0 | 0 | - | 0 | 134 | | (0, 10, 2) | 1 | 10 | 0 | 10 | 135 | | (1, 5, 4) | 2 | 5 | 0 | 10 | 136 | | (0, 10, 5) | 3 | 10 | 0 | 10 | 137 | | (2, 8, 7) | 4 | 8 | 1 | 18 | 138 | | (3, 2, 8) | 5 | 2 | 1 | 18 | 139 | | (5, 3, 10) | 6 | 3 | 3 | 18 | 140 | | (7, 7, 11) | 7 | 7 | 4 | 25 | 141 | | (11, 4, 15) | 8 | 4 | 7 | 29 | 142 | | (12, 11, 17) | 9 | 11 | 7 | 36 | 143 | 144 | Now all that is left to do is take a bottom-up approach using _m[]_ to find the requests in our solution schedule that gave us the maximum value in _m[]_ (which is 36). 145 | 146 | In our first iteration of `find_solution(m, 9)`, we have the request (12, 11, 17). Its value in _m[]_ (36) is greater than _m[9-1]_ so we append it's index to the solution 147 | list and recursively call `find_solution` behind it, i.e.: 148 | 149 | ```python 150 | if v(i) + m[p(i)] > m{i-1]: 151 | return find_solution(m, p(i)) + [i] # append index i to solution 152 | ``` 153 | 154 | This will cause find_solution to go down a path of previously compatible requests that resulted in the maximum schedule value of 36. 155 | 156 | 157 | ![Solution schedule](./assets/final.png) 158 | 159 | 160 | When index _i_ finally reaches 0, it will reach our base case and return all the way 161 | up the call stack, giving us the resulting list of indices `[0, 3, 6, 8]`. 162 | 163 | ## Conclusion 164 | 165 | The `compute_previous` and `opt` steps of this algorithm can be a little confusing, especially in the implementation. This is due to the shift in index between the two functions, necessary 166 | because of the 0th index that is needed in _m[]_ for those requests who do not have previous compatible requests. However, the pseudocode is easy to follow and understand once you 167 | figure out and trace all of the _p(j)_ values and every index of _m[]_. 168 | 169 | The requests included in the final solutions can be printed out themselves by using the code snippet below: 170 | 171 | ```Python 172 | for r in x: 173 | print('({} {} {})'.format(r.start, r.value, r.finish), end=' ') 174 | ``` 175 | -------------------------------------------------------------------------------- /Guide/Dynamic Programming/Educational/Weighted Interval Scheduling/assets/empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Dynamic Programming/Educational/Weighted Interval Scheduling/assets/empty.png -------------------------------------------------------------------------------- /Guide/Dynamic Programming/Educational/Weighted Interval Scheduling/assets/final.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Dynamic Programming/Educational/Weighted Interval Scheduling/assets/final.png -------------------------------------------------------------------------------- /Guide/Dynamic Programming/Educational/Weighted Interval Scheduling/assets/initial1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Dynamic Programming/Educational/Weighted Interval Scheduling/assets/initial1.png -------------------------------------------------------------------------------- /Guide/Dynamic Programming/Educational/Weighted Interval Scheduling/assets/sorted1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Dynamic Programming/Educational/Weighted Interval Scheduling/assets/sorted1.png -------------------------------------------------------------------------------- /Guide/Dynamic Programming/Educational/Weighted Interval Scheduling/solution.py: -------------------------------------------------------------------------------- 1 | import bisect 2 | 3 | 4 | class Request(object): 5 | """ 6 | Simple object to represent a request 7 | """ 8 | 9 | def __init__(self, start, value, finish, previous=None): 10 | self.start = start # start time of the request 11 | self.value = value # value/weight of the request 12 | self.finish = finish # finish time of the request 13 | self.previous = previous # previous compatible request 14 | 15 | 16 | def partition(s, begin, end): 17 | """ 18 | Partially sort an array using a pivot value. Everything to the 19 | left of the pivot will be less than the pivot. Everything to the right 20 | will be greater than the pivot. 21 | :param s: the array to sort 22 | :param begin: the beginning of the section of the array to sort 23 | :param end: the end of the section of the array to sort 24 | :return: the array with sorted section begin-end 25 | """ 26 | pivot = end # set the pivot to the end of the array 27 | pivot_holder_index = begin # place holder for where the pivot will end up 28 | for i in range(begin, end): 29 | if s[i].finish < s[pivot].finish: # if s[i] finish < s[pivot] finish 30 | # switch s[i] and pivot place holder 31 | s[i], s[pivot_holder_index] = s[pivot_holder_index], s[i] 32 | pivot_holder_index += 1 # increase pivot place holder 33 | 34 | # finally, put pivot in its placeholder 35 | s[pivot_holder_index], s[end] = s[end], s[pivot_holder_index] 36 | return pivot_holder_index 37 | 38 | 39 | def quick_sort(s, begin, end): 40 | """ 41 | Quick sort 42 | :param s: the array to sort 43 | :param begin: the beginning of the array 44 | :param end: the ending of the array 45 | :return: the sorted array 46 | """ 47 | # Quick sort 48 | if begin < end: 49 | pivot = partition(s, begin, end) # find a pivot place 50 | quick_sort(s, begin, pivot - 1) # recursive sort sub array on the left 51 | quick_sort(s, pivot + 1, end) # recursive sort sub array on the right 52 | return s 53 | 54 | 55 | def compute_previous(sorted_requests): 56 | """ 57 | Computes the previous compatible request's value for each request 58 | :param sorted_requests: finish-time sorted requests 59 | :return: requests with p(j) filled 60 | """ 61 | start = [i.start for i in sorted_requests] 62 | finish = [i.finish for i in sorted_requests] 63 | 64 | for i in range(0, len(sorted_requests)): 65 | insert_idx = bisect.bisect_right(finish, start[i]) # index of p(i) 66 | # value of request p(i) 67 | sorted_requests[i].previous = insert_idx # assign p(i) value to previous 68 | return sorted_requests 69 | 70 | 71 | def opt(sorted_requests): 72 | """ 73 | Find a maximum value for a set of requests 74 | :param sorted_requests: requests sorted by finish time 75 | :return: opt[0...n] where opt[i] is value of optimal solution for jobs 1...j 76 | """ 77 | m = [0] * (len(sorted_requests) + 1) 78 | for i in range(1, len(sorted_requests) + 1): 79 | r = sorted_requests[i-1] 80 | m[i] = max(r.value + m[r.previous], m[i-1]) 81 | return m 82 | 83 | 84 | def find_solution(sorted_requests, m, i): 85 | """ 86 | Find an optimal schedule with max sum of values 87 | :param sorted_requests: requests sorted by finish time 88 | :param m: array of optimum values for a schedule 1...j 89 | :param i: the index of the last item in sorted_requests 90 | :return: a list of indices 91 | """ 92 | r = sorted_requests[i-1] # there is one less request than elements in m 93 | if i == 0: # base case 94 | return [] 95 | elif r.value + m[r.previous] > m[i-1]: # follow max value result schedule 96 | return find_solution(sorted_requests, m, r.previous ) + [i-1] 97 | else: 98 | return find_solution(sorted_requests, m, i-1) 99 | 100 | 101 | def main(): 102 | x = [Request(2, 8, 7), 103 | Request(0, 10, 2), 104 | Request(1, 5, 4), 105 | Request(12, 11, 17), 106 | Request(5, 3, 10), 107 | Request(3, 2, 8), 108 | Request(11, 4, 15), 109 | Request(7, 7, 11), 110 | Request(0, 10, 5) 111 | ] 112 | 113 | x = quick_sort(x, 0, len(x) - 1) # sort by finish time 114 | x = compute_previous(x) # compute p(j) for every request 115 | m = opt(x) # find max value for scheduling of 1...j 116 | x = find_solution(x, m, len(x)) # find indices of solution schedule 117 | print(x) 118 | 119 | if __name__ == '__main__': 120 | main() 121 | -------------------------------------------------------------------------------- /Guide/Dynamic Programming/Practice/Abbreviation/README.md: -------------------------------------------------------------------------------- 1 | # Abbreviation 2 | 3 | ## Problem 4 | This problem comes from [HackerRank](https://www.hackerrank.com/contests/world-codesprint-6/challenges/abbr). 5 | Category: Dynamic Programming 6 | Difficulty: Medium 7 | 8 | ### Overview 9 | Read the problem statement. The problem is clearly stated and doesn't have a story to add complexity. 10 | 11 | ## Algorithm 12 | ### Overview 13 | This problem is complex because of the edge cases that exist. Some 14 | solutions don't need to worry about them, however when trying to maximize 15 | efficiency it becomes a problem. When I approached the problem I tried to 16 | use a double pointer method, iterating through both strings at the same time. 17 | 18 | The following example will show why this problem is tough to get right 19 | the first time, or second. 20 | 21 | It looks like this (credit to Orion for the edge case), 22 | - A = abcBBcA and B = ABBCA 23 | - p1 (pointer one) = a | p2 (pointer two) = A 24 | - a can be turned into A, so B is possible so far. 25 | - p1 = b | p2 = B 26 | - Another match 27 | - p1 = c | p2 = B 28 | - Move p1 forward (since c can be ignored and deleted). 29 | - p1 = B | p2 = B 30 | - Both are uppercase and they match, so move both pointers forward. 31 | - p1 = B | p2 = C 32 | - Oops! This situation causes a return of "NO", even though it should 33 | print "YES". The problem is that the lowercase 'b' "uses" a slot that 34 | should be used by the uppercase 'B'. Because lowercase characters can 35 | be deleted but uppercase cannot, **every uppercase character must be 36 | used by the algorithm.** 37 | 38 | You can create a kind of "memory" for the algorithm to allow checking of previous characters. 39 | A memory would need the following, 40 | - A unique ID for combinations of lowercase characters. 41 | - A way for uppercase characters to check if they could replace those 42 | lowercase characters that had been previously used. 43 | 44 | It should be possible to create a nearly unique ID for any combination of 45 | lowercase characters. By having a counter that increases as new 46 | characters are added to memory, and multiplying the numeric value 47 | of new characters by it as part of the hash, it will help guarantee a 48 | nearly unique ID for each sequence. Like the following example, 49 | - abc compared to bca where counter starts at one. 50 | - a*counter + b*(counter+1) + c*(counter+2) = 1*1 + 2*2 + 3*3 = 14 51 | - b*counter + c*(counter+1) + a*(counter+2) = 2*1 + 3*2 + 1*3 = 11 52 | 53 | This memory will be stored in an array, like a hash map. By adding 54 | in a modulus operator and by using a prime number for the 55 | modulus, we help guarantee that few IDs will conflict. The prime 56 | number will also match the size of the array used to store the 57 | memory. 58 | 59 | In using this memory we make it possible to get an O(M + N) 60 | time complexity for this problem, since there is no need 61 | to backtrack. 62 | 63 | Assumption: Although lowercase letters may be turned uppercase, 64 | every lowercase character remaining must be deleted. This is not 65 | explicitly stated in the problem, but it is an assumption I am 66 | using. 67 | 68 | ### Pseudo Code 69 | 1. Read in the number of string pairs or "queries" as they are called. 70 | 2. Read in the next pair of strings. Create index pointers and set to 0. 71 | 3. If the char at p1 can match the char p2, 72 | - If the char at p1 is lowercase, add it to memory. 73 | - Else, if it can be replaced by a char in memory, do so, 74 | otherwise clear the memory. 75 | - If the char at the first pointer was lowercase or memory was cleared, 76 | advance both p1 and p2. 77 | 4. Else if both characters are uppercase and don't match, check if 78 | the memory has a char that took the place of the char at p1. 79 | - Continue until the char at p1 is no longer uppercase or 80 | it has reached the end of the string. Advance p1 after each iteration. 81 | 5. If both p1 and p2 are less than the length of their respective strings, 82 | continue at step 3. 83 | 6. If p2 does not equal the length of the final string, print "NO". 84 | 7. If the p1 does not equal the length of the original string, 85 | - Scan through the rest of the original string and check if any 86 | remaining uppercase chars need to (and can be) replaced by 87 | memory. If any cannot be replaced, then print "NO". 88 | 8. If the algorithm has not printed "NO" for the query, print "YES". 89 | 9. Start at step 2 if queries remain. Otherwise finish. 90 | 91 | ### Analysis 92 | For each query the loop iterates a maximum number of O(M + N) times 93 | where M is the length of the original string and N is the length of the 94 | final string. The hash set has O(1) access, insert, and retrieval and 95 | it does not change in size. 96 | 97 | This algorithm wil run in O(M + N) time. The max runtime was .22s for 98 | Test Case #10. 99 | 100 | ## Conclusion 101 | This is a good problem to practice dynamic programming on. Although my 102 | solution was not dynamic programming, it's possible to create a 103 | recursive solution and make it faster by using memoization. For it to be 104 | a dynamic programming solution it would need to break the problem into 105 | sub-problems that can be solved and combined to solve the original problem. 106 | Other things to consider are, 107 | - It would be interesting to see how different solutions match up as the 108 | test cases get much larger in size. My solution is slower than DP solutions 109 | at test case sizes of the ones we used. 110 | - Is there a neat way to distribute this problem across many machines 111 | in chunks of the string? 112 | -------------------------------------------------------------------------------- /Guide/Dynamic Programming/Practice/Abbreviation/solution.java: -------------------------------------------------------------------------------- 1 | import java.io.*; 2 | import java.util.*; 3 | import java.text.*; 4 | import java.math.*; 5 | import java.util.regex.*; 6 | 7 | public class Solution { 8 | 9 | public static class HashMemory { 10 | static int prime = 71327; 11 | static int currentHash = 1; 12 | static int tempHash = 1; 13 | static int currentCount = 1; 14 | static int tempCount = 1; 15 | static byte[] hashSet = new byte[prime]; 16 | 17 | public HashMemory() {} 18 | 19 | public void clearMemory() { 20 | currentHash = 1; 21 | tempHash = 1; 22 | currentCount = 1; 23 | hashSet = new byte[prime]; 24 | } 25 | 26 | public void addLowerCase(char start) { 27 | currentHash = getHash(currentHash, start, currentCount); 28 | currentCount++; 29 | hashSet[currentHash] = 1; 30 | } 31 | 32 | public static int getHash(int current, char next, int currentCount) { 33 | long newHash = (current * Character.getNumericValue(Character.toLowerCase(next)) * currentCount) % prime; 34 | return (int)newHash; 35 | } 36 | 37 | public boolean canClear(char next) { 38 | return checkIfClearable(next, currentCount); 39 | } 40 | 41 | public boolean canClearRemainingChar(char next) { 42 | return checkIfClearable(next, tempCount); 43 | } 44 | 45 | private boolean checkIfClearable(char next, int count) { 46 | int temp = getHash(tempHash, next, count); 47 | return hashSet[temp] == 1; 48 | } 49 | 50 | public void clearChar(char toClear) { 51 | clear(toClear); 52 | } 53 | 54 | public void clearRemainingChar(char toClear) { 55 | clear(toClear); 56 | tempCount++; 57 | } 58 | 59 | public void clear(char toClear) { 60 | hashSet[getHash(tempHash, toClear, currentCount)] = 0; 61 | currentCount--; 62 | } 63 | 64 | public void resetTempHash() { 65 | tempHash = 1; 66 | } 67 | 68 | public int getCurrentCount() { 69 | return currentCount; 70 | } 71 | } 72 | 73 | static Solution.HashMemory mem = new Solution.HashMemory(); 74 | 75 | public static void main(String[] args) { 76 | Scanner in = new Scanner(System.in); 77 | int queryCount = in.nextInt(); 78 | 79 | for (int i = 0; i < queryCount; i++) { 80 | int firstIndex = 0; 81 | int secondIndex = 0; 82 | String first = in.next(); 83 | String second = in.next(); 84 | boolean fail = false; 85 | mem = new Solution.HashMemory(); 86 | 87 | while (firstIndex < first.length() && secondIndex < second.length()) { 88 | char start = first.charAt(firstIndex); 89 | char finish = second.charAt(secondIndex); 90 | boolean hit = false; 91 | 92 | // if: If the characters match or the character from the first string can be capitalized to match. 93 | // else if: If they couldn't match and both characters are upper case, check if the HashMemory can 94 | // find a valid match. 95 | // else: a match is not possible. 96 | if (start == finish || Character.toUpperCase(start) == finish) { 97 | hit = handleNewChar(start, firstIndex, first, finish); 98 | 99 | if (!hit) { 100 | firstIndex++; 101 | secondIndex++; 102 | } 103 | } else if (Character.isUpperCase(start) && Character.isUpperCase(finish)) { 104 | 105 | // Continue to process upper case characters if they exist and can be cleared. 106 | while (Character.isUpperCase(start) && mem.canClear(start)) { 107 | if (!(firstIndex < first.length() && secondIndex < second.length())) 108 | break; 109 | 110 | mem.clearChar(start); 111 | firstIndex++; 112 | hit = true; 113 | start = first.charAt(firstIndex); 114 | } 115 | 116 | if (!hit) { 117 | if (mem.getCurrentCount() == 1) { 118 | fail = true; 119 | } 120 | 121 | firstIndex++; 122 | } 123 | 124 | mem.resetTempHash(); 125 | } else { 126 | firstIndex++; 127 | } 128 | } 129 | 130 | // If the algorithm has not fully processed the second string, it failed. 131 | // Else, check if the first string still has characters that need to be processed (capital chars). 132 | if (secondIndex != second.length()) { 133 | fail = true; 134 | } else { 135 | // Iterate through all characters in the first string. 136 | while (firstIndex < first.length()) { 137 | 138 | char nextC = first.charAt(firstIndex); 139 | 140 | // If the next character is upper case, check if the HashMemory previously satisifed it. 141 | if (Character.isUpperCase(nextC)) { 142 | if (mem.canClearRemainingChar(Character.toLowerCase(nextC))) { 143 | mem.clearRemainingChar(nextC); 144 | } else { 145 | fail = true; 146 | break; 147 | } 148 | } 149 | 150 | firstIndex++; 151 | } 152 | } 153 | 154 | if (fail) 155 | System.out.println("NO"); 156 | else 157 | System.out.println("YES"); 158 | } 159 | } 160 | 161 | // Whenever there is a new match between final and initial characters, the algorithm must 162 | // decide if the new character should be added to the hash, the hash cleared, or a repeated character handled. 163 | public static boolean handleNewChar(char start, int firstIndex, String first, char finish) { 164 | if (Character.isLowerCase(start)) { 165 | mem.addLowerCase(start); 166 | } else { 167 | if (firstIndex + 1 < first.length() && first.charAt(firstIndex + 1) == finish) { 168 | return handleRepeatedNextChar(first, firstIndex) != firstIndex; 169 | } else { 170 | mem.clearMemory(); 171 | } 172 | } 173 | 174 | return false; 175 | } 176 | 177 | // If there's a repeated character then the system should clear a valid previous character if it exists in the 178 | // HashMemory. 179 | public static int handleRepeatedNextChar(String first, int firstIndex) { 180 | char tempChar = first.charAt(firstIndex + 1); 181 | if (mem.canClear(tempChar)) { 182 | mem.clearChar(tempChar); 183 | firstIndex++; 184 | } 185 | 186 | return firstIndex; 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /Guide/Dynamic Programming/Practice/Maximum Path Sum/README.md: -------------------------------------------------------------------------------- 1 | # Maximum Path Sum 2 | This problem comes from [HackerRank](https://www.hackerrank.com/contests/projecteuler/challenges/euler018) and is categorized as a Dynamic Programming Problem 3 | 4 | ##### Tags 5 | [Dynaimc Programming](https://en.wikipedia.org/wiki/Dynamic_programming) / [Array Construction](https://en.wikipedia.org/wiki/Array_data_structure) 6 | 7 | ## Problem 8 | ### Statement 9 | 10 | By starting at the top of the triangle below and moving to adjacent numbers on the row below, the maximum total from top to bottom is:
11 |
12 | The path is denoted by numbers in bold. 13 |

14 |     **3**
15 |    **7** 4
16 |   2 **4** 6
17 | 8 5 **9** 3 18 | 19 | That is, 3 + 7 + 4 + 9 = 23 20 | Find the maximum total from top to bottom of the triangle given in input. 21 | 22 | #### Input Format 23 | 24 | The first line contains _**T**_, the number of testcases, For each testcase: 25 | The first line contains _**N**_, the number of rows in the triangle. 26 | For the next _**N**_ lines, the _**i**_'th line contains _**i**_ numbers. 27 | 28 | ### Overview 29 | This problem is relatively straightforward. Starting from the top (or bottom), find the maximum path to the opposite side. 30 | 31 | 1. Store the values in a 2d array 32 | 2. Loop through the 2d array again, calculating the sums at each level 33 | 3. Scan the bottom level and print the maximum value 34 | 35 | ## Algorithm 36 | 37 | ### Overview 38 | 39 | The Algorithm for this problem is quite nice. All that is necessary is a 2d array. 40 | I initally set the size to the maximum possible size the problem describes. 41 | 42 | I then loop through the 2d Array storing the values, using variables j and k where 43 | _0 <= j < n_ (where n is the number of rows in the triangle) and 44 | _0 <= k <= j_ 45 | 46 | using the example from above this will appear like this: 47 | 48 | 3
49 | 7 4
50 | 2 4 6
51 | 8 5 9 3
52 | 53 | Once the original triangle from the problem is constructed, We must loop through again and find the sums at each 54 | level. In order to do this we use the same looping format as shown above. At each level we'll want to change the current value 55 | to the maximum of the current value plus the value above it to the left, or the value above it to the right. We repeat this 56 | for every row, building the sums at each level as we go down. In this portion you must be wary of the edge cases. 57 | (when k = 0 or k = j). In this circumstance you must set the left or right side to 0 in order to avoid an array out of 58 | bounds error. 59 | 60 | Again using the example, the folloing will be the result of compiling the sums. 61 | 62 | 3
63 | 10 7
64 | 12 14 13
65 | 20 19 23 16
66 | 67 | Lastly, all that is needed is to look through the bottom row and find the maximum value. Which in this case is 23. 68 | 69 | ### Pseudo Code 70 | 1. Create 2d Array of size max size 71 | 2. Read in and fill array with the triangle given in the problem 72 | 3. Rescan through triangle computing the sum of the current value based on whats is above it to the left and right. 73 | 4. Scan through the bottom row to find the maximum value (this step can be cut out by keeping track during the additions) 74 | 75 | ### Analysis 76 | 77 | Within the code the bottlenecks only show up when running through the 2d Array. But as you can see, the way the 2d array 78 | is set up, each item in the array is only being looked at once. So each scan through the 2d array is of length N. Since we 79 | do this scan twice (once to read in the values, another to calculate the sums), the Big-O notation is O(2N) -> O(N). 80 | 81 | ## Conclusion 82 | 83 | If you're just beginning to learn dynamic programming problems, this is a great place to start. The solution to this problem 84 | is easy to visualize, and you can see how building upon sub-problems leads to a reletively elegant solution to a somewhat challenging problem. 85 | 86 | -------------------------------------------------------------------------------- /Guide/Dynamic Programming/Practice/Maximum Path Sum/solution.java: -------------------------------------------------------------------------------- 1 | import java.io.*; 2 | import java.util.*; 3 | 4 | 5 | public class Solution { 6 | 7 | 8 | public static void main(String[] args) { 9 | Scanner scan = new Scanner(System.in); 10 | int cases = scan.nextInt(); 11 | for(int i = 0; i < cases; i++) { 12 | 13 | int n = scan.nextInt(); 14 | int[][] numbers = new int[101][101]; 15 | 16 | for(int j = 0; j < n; j++) { 17 | for(int k = 0; k <= j; k++) { 18 | numbers[j][k] = scan.nextInt(); 19 | } 20 | } 21 | 22 | for(int j = 1; j < n; j++) { 23 | for(int k = 0; k <= j; k++) { 24 | int left; 25 | int right; 26 | if(k == 0) { 27 | left = 0; 28 | } else { 29 | left = numbers[j-1][k-1]; 30 | } 31 | if(k == j) { 32 | right = 0; 33 | } else { 34 | right = numbers[j-1][k]; 35 | } 36 | 37 | numbers[j][k] += Math.max(left, right); 38 | } 39 | } 40 | 41 | int max = 0; 42 | for(int j = 0; j < n; j++) { 43 | if(numbers[n-1][j] > max) { 44 | max = numbers[n-1][j]; 45 | } 46 | } 47 | System.out.println(max); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Guide/Dynamic Programming/Practice/Nikita And The Game/README.md: -------------------------------------------------------------------------------- 1 | # Nikita And The Game 2 | This problem comes from [HackerRank](https://www.hackerrank.com/challenges/array-splitting) 3 | Category: Dynamic Programming 4 | Difficulty: Medium 5 | 6 | ## Problem 7 | 8 | 9 | ### Overview 10 | This was problem of medium difficulty. There are multiple ways to solve this problem and on top of that there are a couple tricks one can implemented that, in certain scenarios, the solution can be determined early on, allowing for a quick end to the program. 11 | 12 | After reading the problem statement including input, output and examples, these are my initial impressions: 13 | - It's important to note that the array can only be partitioned. As opposed to reordered. That actually simplifies things greatly. 14 | - How should we handle an array consisting of all zeros? 15 | 16 | ## Algorithm 17 | ### Overview 18 | If we have to partition an array into sub arrays with equal size then we can deduce this formula: 19 | - Let the sum of the values of the sub arrays equal x. Then the sum of values of the full array must equal 2x. 20 | - From this we can see that the sum of values of an array must even if we are to partition it evenly. 21 | - While the sum of the values of an array must be even, the size of the array does not need to be. 22 | - Is there more than 1 way an array can be partitioned into two sub arrays of equal sum values? (The answer is no, and that’s good to know. This means that once we find a way to partition an array correctly, we don't have to keep checking that array.) 23 | 24 | ### Terminology 25 | **Main Array**: An array from which two sub-arrays are partitioned from 26 | **SumVal**: The sum of the values of an array 27 | **Goal**: The value we want to reach with our sub array 28 | ### Pseudo Code 29 | For each test case: 30 | 31 | #### Stage 1 32 | 1. Read in size of array (Keep track of **SumVal** of initial array) 33 | - If **SumVal** equals 0 then the solution is the size of the array minus 1 34 | - If **SumVal** is an odd number then the solution is 0 35 | - Else let **Main Array** equal the initial array and **Goal** equal the **SumVal** of the initial array divided by 2 36 | 37 | #### Stage 2 38 | With **Main Array** and **Goal** 39 | 1. If **Main Array**'s size is less than 1 or **Goal** equals 0 then return 0 40 | - Iterate through **Main Array** from left to right adding the values at that point to **SumVal** and each time check: 41 | 1. If **SumVal** > **Goal** we cannot split **Main Array**, return 0 42 | - If **SumVal** == **Goal** we can split **Main Array** 43 | 1. Call this stage again with **Main Array** = the sub part of the **Main Array** already iterated through and **Goal** = **Goal**/2 and return 1 + result 44 | - Call this stage again with **Main Array** = the sub part of the **Main Array** not yet iterated through and **Goal** = **Goal**/2 and return 1 + result 45 | - If **SumVal** < **Goal** continue 46 | 47 | ### Analysis 48 | By looking at the source code, we can see the only looping that happens per test case is: 49 | ```CPP 50 | for (int seq_index = 0; seq_index < seq_size; seq_index++) { 51 | //... The bulk of the code here 52 | } 53 | ``` 54 | from this we can conclude that the complexity of this algorithm is _O(N)_ 55 | ## Conclusion 56 | This solution passes the problem tests with the worst being Test Case 10 at 0.00s time. 57 | -------------------------------------------------------------------------------- /Guide/Dynamic Programming/Practice/Nikita And The Game/solution.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include // to_string() 5 | 6 | typedef unsigned long long int seq_var; 7 | typedef unsigned int element_var; 8 | typedef std::vector::iterator seq_itr; 9 | 10 | 11 | int countSplits(seq_itr begin, seq_itr end, seq_var sum_goal) { 12 | seq_var element_sum = 0; 13 | 14 | if ((std::distance(begin, end) < 1) || (sum_goal == 0)) { 15 | return 0; 16 | } 17 | 18 | 19 | for (seq_itr it = begin; it != end; ++it) { 20 | element_sum += *it; 21 | 22 | 23 | if (element_sum > sum_goal) { 24 | return 0; 25 | } else if (element_sum == sum_goal) { 26 | seq_var newSumGoal = sum_goal / 2; 27 | 28 | 29 | int leftPartSum = countSplits(begin, it, newSumGoal); 30 | ++it; 31 | int rightPartSum = countSplits(it, end, newSumGoal); 32 | 33 | // Return 1 + MAX(leftPartSum,rightPartSum) aka the greatest amount of 34 | // splits that can happen 35 | if (leftPartSum > rightPartSum) { 36 | return 1 + leftPartSum; 37 | } else { 38 | return 1 + rightPartSum; 39 | } 40 | } 41 | 42 | // The only condition not checked is if element_sum < sum_goal. In that 43 | // case, we just want to keep iteratiing through the vector 44 | } 45 | 46 | return 0; 47 | } 48 | 49 | int main() { 50 | int test_count; // The number of test cases 51 | 52 | std::cin >> test_count; 53 | 54 | // For each test case 55 | for (int test_index = 0; test_index < test_count; test_index++) { 56 | int seq_size; // Size of the sequence (array) we are 57 | // about to take in. 58 | 59 | std::cin >> seq_size; 60 | std::string str_test_index = std::to_string(test_index); 61 | 62 | std::vector sequence; 63 | 64 | element_var element; 65 | seq_var element_sum = 0; 66 | 67 | for (int seq_index = 0; seq_index < seq_size; seq_index++) { 68 | std::cin >> element; 69 | element_sum += element; 70 | sequence.push_back(element); 71 | } 72 | 73 | // If the sum of the elements is 0, then we have a list of all 0s. For an 74 | // array with size N, and all values=0, the max amount of splits is N-1. 75 | if (element_sum == 0) { 76 | std::cout << (seq_size - 1) << std::endl; 77 | continue; 78 | } 79 | 80 | // If the sum of the elements is NOT even then we cannot split the sequence 81 | // into two partitions of equal sums. So we can stop here, print out 0 and 82 | // move on to the next test case. 83 | if (element_sum % 2 != 0) { 84 | std::cout << 0 << std::endl; 85 | continue; 86 | } 87 | 88 | std::cout << 89 | countSplits(sequence.begin(), sequence.end(), element_sum / 2) << std::endl; 90 | } 91 | 92 | return 0; 93 | } 94 | -------------------------------------------------------------------------------- /Guide/Dynamic Programming/README.md: -------------------------------------------------------------------------------- 1 | # Dynamic Programming Guide 2 | 3 | ## Overview 4 | Dynamic Programming - An algorithm that: 5 | 6 | * Solves a problem by breaking it into smaller subproblems that can then be used to find the answer to the original problem. 7 | 8 | * Gets rid of recursion by using [memoization](http://stackoverflow.com/questions/1988804/what-is-memoization-and-how-can-i-use-it-in-python). 9 | 10 | ## Additional Resources 11 | 12 |

The links bellow are resources that can help you more fully understand 13 | dynamic programming. 14 | 15 | [wikipedia dynamic programming](https://en.wikipedia.org/wiki/Dynamic_programming) 16 | 17 | [topcoder dynamic programming](https://www.topcoder.com/community/data-science/data-science-tutorials/dynamic-programming-from-novice-to-advanced/) -------------------------------------------------------------------------------- /Guide/GraphTheory/JackGoesToRapture/README.md: -------------------------------------------------------------------------------- 1 | # Jack goes to Rapture 2 | 3 | ## Problem 4 | This problem comes from [HackerRank](https://www.hackerrank.com/challenges/jack-goes-to-rapture). 5 | Category: Graph Theory 6 | Difficulty: Medium 7 | 8 | ### Overview 9 | Essentially the problem is giving the following challenge, 10 | 11 | 1. Assume you are given a list of edges for vertices from 1...n. 12 | 2. Find the weight of the path starting at 1 and ending at n such that the 13 | cost of going from 1 to n is minimized. If no such path exists print "NO 14 | PATH EXISTS". 15 | 3. The cost of a path is the maximum weight (cost) of any edge in that path. 16 | 4. No two edges have the same cost and no cost is negative. 17 | 18 | ## Algorithm 19 | ### Overview 20 | The data structure best suited to this problem is Union-Find (UF). UF is 21 | a data structure that efficiently merges groupings of objects together 22 | and checks to see if two objects share the same group. In this case each 23 | integer represents a vertex. It has the following operations, 24 | - find(int i): returns the ID of the group that 'i' is in. 25 | - count(): returns the number of groupings 26 | - connected(int a, int b): returns true if 'a' and 'b' are in the same group. 27 | - union(int a, int b): merges the groups of 'a' and 'b'. 28 | 29 | NOTE: I used the implementation of UF from a [Princeton course](http://algs4.cs.princeton.edu/15uf/UF.java.html). 30 | 31 | Using UF we can union the edges with the smallest weight until we get a 32 | path from 1 to n. By using UF in this way, we minimize the cost of getting 33 | from 1 to n. 34 | 35 | Assumption: We don't care if we get extra edges in our set of minimal cost 36 | edges because fair cost is the max edge cost among all edges. By going from 37 | smallest cost to largest, if we reach the destination we can assume that 38 | all previously used edges have a lower cost. 39 | 40 | ### Pseudo Code 41 | 1. Read in values 3 at a time, creating edges and adding them to a list. 42 | 2. Sort all edges in ascending order by weight (cost). 43 | 3. Iterate through the list of edges, calling union on each pair of vertices 44 | until vertices 1 and n are connected, or until reaching the end of the list. 45 | 4. If there is a path from 1 to n, print the max edge value from that 46 | path. Else, print "NO PATH EXISTS". 47 | 48 | ### Analysis 49 | The best sorting algorithms under average conditions achieve a time 50 | complexity of _O(m*log(m))_. Where m is the number of edges. 51 | Union-Find has a time complexity of _O(log(m))_. 52 | We apply Union-Find a maximum of m times during our loop through the list 53 | of edges. 54 | So the resulting time complexity of this solution is _O(m*log(m))_. 55 | 56 | The longest running test case was #15 at 2.3s. This solution gets a full 57 | score of 80 on HackerRank. 58 | 59 | ## Conclusion 60 | This is a fun problem because it both tests your knowledge of graph theory 61 | and your knowledge of different data structures. Although it can be fun to 62 | try, as far as we know there is no _O(m)_ or _O(log(m))_ solution to this 63 | problem. Other interesting challenges to consider are as follows. 64 | - Does the solution change if edges can have the same weight? If so, how? 65 | - How would you solve this problem if there were so many edges that you 66 | could not fit them all on the same machine? 67 | -------------------------------------------------------------------------------- /Guide/GraphTheory/JackGoesToRapture/solution.java: -------------------------------------------------------------------------------- 1 | import java.io.*; 2 | import java.util.*; 3 | import java.text.*; 4 | import java.math.*; 5 | import java.util.regex.*; 6 | 7 | public class Solution { 8 | 9 | // This implementation of UnionFind http://algs4.cs.princeton.edu/15uf/UF.java.html 10 | public static class UF { 11 | 12 | private int[] parent; // parent[i] = parent of i 13 | private byte[] rank; // rank[i] = rank of subtree rooted at i (never more than 31) 14 | private int count; // number of components 15 | 16 | /** 17 | * Initializes an empty union–find data structure with {@code n} sites 18 | * {@code 0} through {@code n-1}. Each site is initially in its own 19 | * component. 20 | * 21 | * @param n the number of sites 22 | * @throws IllegalArgumentException if {@code n < 0} 23 | */ 24 | public UF(int n) { 25 | if (n < 0) throw new IllegalArgumentException(); 26 | count = n; 27 | parent = new int[n]; 28 | rank = new byte[n]; 29 | for (int i = 0; i < n; i++) { 30 | parent[i] = i; 31 | rank[i] = 0; 32 | } 33 | } 34 | 35 | /** 36 | * Returns the component identifier for the component containing site {@code p}. 37 | * 38 | * @param p the integer representing one site 39 | * @return the component identifier for the component containing site {@code p} 40 | * @throws IndexOutOfBoundsException unless {@code 0 <= p < n} 41 | */ 42 | public int find(int p) { 43 | validate(p); 44 | while (p != parent[p]) { 45 | parent[p] = parent[parent[p]]; // path compression by halving 46 | p = parent[p]; 47 | } 48 | return p; 49 | } 50 | 51 | /** 52 | * Returns the number of components. 53 | * 54 | * @return the number of components (between {@code 1} and {@code n}) 55 | */ 56 | public int count() { 57 | return count; 58 | } 59 | 60 | /** 61 | * Returns true if the the two sites are in the same component. 62 | * 63 | * @param p the integer representing one site 64 | * @param q the integer representing the other site 65 | * @return {@code true} if the two sites {@code p} and {@code q} are in the same component; 66 | * {@code false} otherwise 67 | * @throws IndexOutOfBoundsException unless 68 | * both {@code 0 <= p < n} and {@code 0 <= q < n} 69 | */ 70 | public boolean connected(int p, int q) { 71 | return find(p) == find(q); 72 | } 73 | 74 | /** 75 | * Merges the component containing site {@code p} with the 76 | * the component containing site {@code q}. 77 | * 78 | * @param p the integer representing one site 79 | * @param q the integer representing the other site 80 | * @throws IndexOutOfBoundsException unless 81 | * both {@code 0 <= p < n} and {@code 0 <= q < n} 82 | */ 83 | public void union(int p, int q) { 84 | int rootP = find(p); 85 | int rootQ = find(q); 86 | if (rootP == rootQ) return; 87 | 88 | // make root of smaller rank point to root of larger rank 89 | if (rank[rootP] < rank[rootQ]) parent[rootP] = rootQ; 90 | else if (rank[rootP] > rank[rootQ]) parent[rootQ] = rootP; 91 | else { 92 | parent[rootQ] = rootP; 93 | rank[rootP]++; 94 | } 95 | count--; 96 | } 97 | 98 | // validate that p is a valid index 99 | private void validate(int p) { 100 | int n = parent.length; 101 | if (p < 0 || p >= n) { 102 | throw new IndexOutOfBoundsException("index " + p + " is not between 0 and " + (n-1)); 103 | } 104 | } 105 | } 106 | 107 | /** 108 | * An Edge object is used to organize the solution to this problem 109 | * and make it easy to sort edges using a Comparator. 110 | */ 111 | public static class Edge { 112 | int vert_a; 113 | int vert_b; 114 | int weight; 115 | int cost; 116 | 117 | /** 118 | * Constructor for Edge. 119 | * 120 | * @param a is the first vertex of the edge. 121 | * @param b is the second vertex of the edge. 122 | * @param w is the weight of the edge. 123 | */ 124 | public Edge(int a, int b, int w) { 125 | vert_a = a; 126 | vert_b = b; 127 | weight = w; 128 | } 129 | 130 | public int get_a() { 131 | return vert_a; 132 | } 133 | 134 | public int get_b() { 135 | return vert_b; 136 | } 137 | 138 | public int get_weight() { 139 | return weight; 140 | } 141 | } 142 | 143 | /** 144 | * Implementation of a Comparator so that two edges can be 145 | * compared. Used for sorting. 146 | */ 147 | public static class EdgeComparator implements Comparator { 148 | @Override 149 | public int compare(Edge a, Edge b) { 150 | if (a.get_weight() > b.get_weight()) 151 | return 1; 152 | return -1; 153 | } 154 | } 155 | 156 | public static void main(String[] args) { 157 | /* Enter your code here. Read input from STDIN. Print output to STDOUT. Your class should be named Solution. */ 158 | 159 | Scanner in = new Scanner(System.in); 160 | 161 | int station_count = in.nextInt(); // The number of stations. 162 | int rows = in.nextInt(); // The number of edges. 163 | int min_cost = 0; 164 | ArrayList edges = new ArrayList(); 165 | UF groupings = new UF(station_count); 166 | 167 | // Scan in and create all edges. 168 | for (int i = 0; i < rows; i++) { 169 | int a = in.nextInt(); 170 | int b = in.nextInt(); 171 | int w = in.nextInt(); 172 | Edge n = new Edge(a, b, w, 0); 173 | 174 | edges.add(n); 175 | } 176 | 177 | // Sort edges in increasing order by length. 178 | Collections.sort(edges, new EdgeComparator()); 179 | 180 | // Use UnionFind to pick the set of edges that reaches the destination with minimal cost. 181 | for (int i = 0; i < edges.size(); i++) { 182 | // If a path already exists, end the loop. 183 | if (groupings.connected(0, station_count - 1)) 184 | break; 185 | 186 | Edge next = edges.get(i); 187 | 188 | if (!groupings.connected(next.get_a() - 1, next.get_b() - 1)) { 189 | min_cost = next.get_weight(); 190 | groupings.union(next.get_a() - 1, next.get_b() - 1); 191 | } 192 | } 193 | 194 | // If a path exists to the destination, success, otherwise it fails. 195 | if (min_cost == 0 || !groupings.connected(0, station_count - 1)) 196 | System.out.println("NO PATH EXISTS"); 197 | else 198 | System.out.println(min_cost); 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /Guide/GraphTheory/Journey to the Moon/README.md: -------------------------------------------------------------------------------- 1 | # Journey to the Moon 2 | This problem comes from [HackerRank](https://www.hackerrank.com/challenges/journey-to-the-moon) 3 | 4 | Category: Graph Theory 5 | 6 | Difficulty: Medium 7 | 8 | ## Problem 9 | 10 | ### Overview 11 | Given 'N' objects numbered from 0 to 'N' - 1 and 'I' pairs taken from those 'N' objects. 12 | - Determine which of the objects are grouped together. 13 | - A grouping is formed through pairs. 14 | - For example, if 1, 2, and 3 are objects and there are two pairs (1,2) and (2,3), then all three objects are in the same group even though 1 and 3 aren't directly connected. 15 | - Compute the number of ways that a pair of objects from different groups can be picked. 16 | 17 | ### Input Format 18 | - The first line contains two integers 'N' and 'I' separated by a space. 19 | - The next 'I' lines contain two integers 'A' and 'B' (in this order) each separated by a space. 20 | - 'A' and 'B' are both objects that are paired. 21 | 22 | ### Constraints 23 | - 1 <= N <= 10^5 24 | - 1 <= I <= 10^4 25 | 26 | ### Output Format 27 | - A single line with the computed value. 28 | 29 | ## Algorithm 30 | 31 | ### Overview 32 | The core of this solution is the adjacency list that holds pair data. 33 | Given this input: 34 | 35 | 5 2 36 | 37 | 0 2 38 | 39 | 1 2 40 | 41 | The adjacency list should look like this: 42 | 43 | | 0 | 1 | 2 | 3 | 4 | 44 | | --- | --- | --- | --- | --- | 45 | | 2 | 2 | 0 | _ | _ | 46 | | _ | _ | 1 | _ | _ | 47 | 48 | In order to solve this problem, one must first figure out which objects are in a group together and how large the groups are. 49 | The size of the groups is vital, because this problem ultimately boils down to combinations, which can be determined through the multiplication of different group sizes. 50 | For instance, in the above example there are three groups with sizes 3, 1, and 1 respectively. 51 | Order doesn’t matter, so the method for determining the number of ways there are to pick pairs is as follows: 52 | 53 | 3 * (1 + 1) + 1 * (1) = 7 54 | 55 | Or more generally: 56 | 57 | Group 1 * (Group 2 + Group 3) + Group 2 * (Group 1) 58 | 59 | There are three requirements for determining the size of a given group. 60 | - A counter to store the size. 61 | - A hash map to keep track of which objects have already been accounted for. 62 | - A stack to keep track of which object entries in the adjacency list still need to be visited. 63 | 64 | In order to prevent an infinite loop, values must be removed from the adjacency list after they are operated on. 65 | But this creates an issue in that groups with only a single object in them appear to be empty according to the adjacency list. 66 | So before doing operations on the adjacency list, one must first iterate through it and create a new group of size one for every empty entry. 67 | 68 | ### Pseudo Code 69 | Given an adjacency list 'pairs' implemented using a 2D vector, a vector 'countries' that will contain object group sizes, a variable 'numAsts' that contains the number of objects, and a variable 'result' that stores the number of ways a pair can be chosen. 70 | - The first dimension of 'pairs' contains an entry corresponding to each of the N objects. 71 | - The second dimension of 'pairs' contains all objects that the given object is paired with. 72 | - For each entry in 'pairs' that is empty, push a value of 1 to 'countries.' 73 | - For each entry in 'pairs.' 74 | - If the entry is not empty. 75 | - Set the counter 'countryAsts' to 0. 76 | - Create a hashMap 'tempMap.' 77 | - Create a vector 'targetAsts.' 78 | - Add the current entry to 'targetAsts.' 79 | - Add the current entry to 'tempMap.' 80 | - Add 1 to 'countryAsts.' 81 | - While 'targetAsts' isn't empty. 82 | - Pop the last element of 'targetAsts' into 'current.' 83 | - While the entry corresponding to 'current' isn't empty. 84 | - Pop the last element of the entry corresponding to 'current' into 'tempAst.' 85 | - If 'tempAst' isn't in 'tempMap.' 86 | - Add 'tempAst' to 'targetAsts.' 87 | - Add 'tempAsp' to 'tempMap.' 88 | - Increment 'countryAsts' by 1. 89 | - Add 'countryAsts' to 'countries.' 90 | - For each element in 'countries.' 91 | - Subtract the current element from 'numAsts.' 92 | - Add the product of 'numAsts' and the current element. 93 | 94 | ### Analysis 95 | This algorithm runs in linear time. 96 | There are two simple loops that contain only constant time operations, the loop that searches for single object groups and the loop that calculates 'result.' 97 | While the portion of the algorithm that calculates group sizes does feature a loop containing nested loops, it visits each value in 'pairs' a constant number of times. 98 | Given that 'pairs' contains 'I' * 2 values, it can be considered a linear time operation. 99 | 100 | ## Conclusion 101 | This problem is particularly good at highlighting the importance of not focusing on the most obvious parts of a solution. 102 | For instance, the last step is calculating the number of possible unique pairs, an operation that can be accomplished with four lines of code. 103 | Because of its simplicity, it is easy to code the last step without thinking about the correct method to use. 104 | Given a list of group sizes, the naive method involves adding the product of a given element and all other elements until the end of the list, for each element in the list. 105 | This results in an O(G^2) operation, where G is the number of groups. 106 | This is far inferior to the method shown in this solution, which is an O(G) operation. 107 | -------------------------------------------------------------------------------- /Guide/GraphTheory/Journey to the Moon/solution.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | typedef unsigned long moonType; 7 | 8 | int main() 9 | { 10 | moonType numAsts; // Number of astronauts (N) 11 | moonType numPairs; // Number of pairs (I) 12 | moonType astOne; // First astronaut in each input line 13 | moonType astTwo; // Second astronaut in each input line 14 | moonType countryAsts; // Number of astronauts in a given country 15 | moonType tempAst; // Index of current astronaut from 'pairs' 16 | moonType current; // Index of current astronaut from 'targetAsts' 17 | long long result = 0; // Number of ways to choose pairs of astronauts 18 | 19 | /* 20 | 'pairs' is an adjacency list implemented with a 2D vector. One 21 | dimension of ‘pairs’ has an entry corresponding to each of the 22 | astronauts. The second dimension holds all of the astronauts that a 23 | given astronaut is paired with. 24 | */ 25 | std::vector > pairs; 26 | 27 | /* 28 | ‘countries’ is a vector that holds the number of astronauts for a 29 | given country. 30 | */ 31 | std::vector countries; 32 | 33 | std::cin >> numAsts >> numPairs; 34 | 35 | // Initializing 2nd dimension of 'pairs' 36 | for(int i = 0; i < numAsts; i++) 37 | { 38 | std::vector tempVec; 39 | pairs.push_back(tempVec); 40 | } 41 | 42 | // Reading input into 'pairs' 43 | for(int i = 0; i < numPairs; i++) 44 | { 45 | std::cin >> astOne >> astTwo; 46 | 47 | /* 48 | ‘astTwo’ is added to the second dimension of ‘astOne’ and vice 49 | versa. This ensures that astronauts paired with ‘astTwo’ but not 50 | ‘astOne’ are still counted as being in the same country as 51 | ‘astOne,’ no matter where ‘astOne’ and ‘astTwo’ are in relation to 52 | each other within 'pairs.' 53 | */ 54 | pairs.at(astOne).push_back(astTwo); 55 | pairs.at(astTwo).push_back(astOne); 56 | } 57 | 58 | // Accounting for countries with a single astronaut 59 | for(unsigned long i = 0; i < numAsts; i++) 60 | { 61 | // If an astronaut is not paired with anyone 62 | if(pairs.at(i).empty()) 63 | countries.push_back(1); 64 | } 65 | 66 | // Determining country sizes 67 | for(unsigned long i = 0; i < numAsts; i++) 68 | { 69 | if(!pairs.at(i).empty()) 70 | { 71 | countryAsts = 0; 72 | // Used to determine if an astronaut is already in the country 73 | std::map tempMap; 74 | // Stores astronaut entries in 'pairs' that need to be visited 75 | std::vector targetAsts; 76 | 77 | // Add the starting astronaut to 'targetAsts' 78 | targetAsts.push_back(i); 79 | // Add the starting astronaut to 'tempMap' 80 | tempMap.insert(std::pair(i, 1)); 81 | // Increase country size by one 82 | countryAsts++; 83 | 84 | /* 85 | This code searches through entries in ‘pairs’ and performs 86 | three main operations. The first operation checks if any 87 | astronauts it finds are already accounted for in the current 88 | country, if so, they are deleted and the program moves on. If 89 | not, the other two operations are performed. The second 90 | operation adds the current astronaut to the vector of 91 | astronaut entries to be visited. The third operation adds the 92 | current astronaut to the hash map that accounts for astronaut 93 | presence in the country. This repeats until 'targetAsts' is 94 | empty. 95 | */ 96 | while(!targetAsts.empty()) 97 | { 98 | // Store the last element of 'targetAsts' 99 | current = targetAsts.back(); 100 | // Remove the last element of 'targetAsts' 101 | targetAsts.pop_back(); 102 | 103 | // While the current entry isn't empty 104 | while(!pairs.at(current).empty()) 105 | { 106 | // Store the last element of the current entry 107 | tempAst = pairs.at(current).back(); 108 | 109 | // If 'tempAst' isn't in 'tempMap' 110 | if(tempMap.find(tempAst) == tempMap.end()) 111 | { 112 | targetAsts.push_back(tempAst); 113 | tempMap.insert(std::pair(tempAst, 1)); 114 | countryAsts++; 115 | } 116 | 117 | // Remove the last element from the current entry 118 | pairs.at(current).pop_back(); 119 | } 120 | } 121 | 122 | countries.push_back(countryAsts); 123 | } 124 | } 125 | 126 | /* 127 | This calculates the number of permutations for the current country 128 | through the distributive property. ‘numAsts’ represents the number of 129 | astronauts left to operate on. Subtracting the size of the current 130 | country from ‘numAsts’ results in the number of astronauts aside from 131 | the current country. By multiplying the size of the current country 132 | and the number of remaining astronauts together it is possible to 133 | avoid having to distribute each entry individually. 134 | */ 135 | for(unsigned long i = 0; i < countries.size(); i++) 136 | { 137 | numAsts -= countries.at(i); 138 | result += numAsts * countries.at(i); 139 | } 140 | 141 | std::cout << result << std::endl; 142 | 143 | return 0; 144 | } 145 | -------------------------------------------------------------------------------- /Guide/GraphTheory/README.md: -------------------------------------------------------------------------------- 1 | # Graph Theory Guide 2 | 3 | ## Overview 4 | At their core, graphs are sets of objects (vertices) with connections (edges) between them. 5 | For example, a roadmap could be represented by a graph with cities as vertices and highways as edges. 6 | A program dealing with this example would have at least two data structures. 7 | The first would store information about cities, like their name and which highways they are connected to. 8 | The second would store information about highways, like their name, how long they are, and which cities they connect. 9 | 10 | ## Additional Resources 11 | This section contains links to resources about graphs. 12 | 13 | ### Introduction 14 | [Examples of Graph Problems] (http://world.mathigon.org/Graph_Theory) - 15 | This webpage discusses some of the common types of problems that can be represented by graphs. 16 | 17 | [Video on Graph Basics] (https://www.youtube.com/watch?v=HmQR8Xy9DeM) - 18 | This video covers basic mechanics required to program a graph. 19 | 20 | [In-Depth Material] (https://www.tutorialspoint.com/graph_theory/index.htm) 21 | 22 | ### Data Structures 23 | When solving any programming problem, one of the first things that must be determined is how data will be stored. 24 | This section contains links to information about common graph data structures. 25 | 26 | [Adjacency Lists and Matrices] (http://www.geeksforgeeks.org/graph-and-its-representations/) 27 | 28 | ### Algorithms 29 | Once a dataset has been recognized as a graph, it is possible to use graph algorithms to solve problems that relate to that dataset. 30 | This section contains links to information about common graph algorithms. 31 | 32 | [Dijkstra's Algorithm] (http://www.geeksforgeeks.org/greedy-algorithms-set-6-dijkstras-shortest-path-algorithm/) - 33 | Dijkstra's algorithm is used to find the shortest path between two vertices in a graph. 34 | Given a graph with airports as vertices and flights as edges (where edge length is flight price), it would be possible to find the cheapest way to get from one airport to another. 35 | 36 | [Kruskal's Algorithm] (http://www.geeksforgeeks.org/greedy-algorithms-set-2-kruskals-minimum-spanning-tree-mst/) - 37 | Kruskal's algorithm is used to find the [minimum spanning tree] (http://algs4.cs.princeton.edu/43mst/) of a given graph. 38 | -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Interval Partitioning/README.md: -------------------------------------------------------------------------------- 1 | # Interval Partitioning 2 | 3 | This problem is introduced in [Dr. Gelfond Applied Algorithms](http://redwood.cs.ttu.edu/~mgelfond/FALL-2012/slides.pdf). 4 | 5 | Category: Greedy 6 | 7 | Difficulty: Medium 8 | 9 | 10 | ## Problem 11 | 12 | Given a set _J = {i1 . . . , in}_ of jobs where each request _i_ is 13 | associated with an interval _[s(i), f(i)_] and each request requires one 14 | of many available resources in _R_, schedule all jobs using as few resources as possible. 15 | 16 | ### Overview 17 | 18 | Examples of this problem would be: scheduling classes using as few classrooms as possible (here, classrooms are the resources), or 19 | allocating printing jobs using as few printers as possible (the printers are the resources), etc. 20 | 21 | So we have a set of resources and a set of jobs that have a start time _s(i)_ and a finish time _f(i)_, and we need to complete all of 22 | these jobs while using as few resources as possible. If I have a request that starts at 12:00PM and finishes at 2:00PM, and another that 23 | starts at 3:00PM and finishes at 4:00PM, I will only have to use one resource because the two jobs do not overlap. However, if the second request were 24 | to start at 1:30PM, I would then need to use two resources because the two jobs overlap. 25 | 26 | Likewise, if we have a set of jobs: 27 | 28 | J = [ (12PM, 3PM), (2PM, 5PM), (4PM, 6PM) ], 29 | 30 | we would then need to use at least 3 resources because all three jobs overlap. 31 | From this we can draw the conclusion that the amount of resources we need will be equal to the amount of jobs in _J_ that overlap. 32 | 33 | ## Algorithm 34 | ### Overview 35 | 36 | Since each request will have a definitive start time and finish time, it makes sense that we schedule the jobs that start the earliest 37 | first. As we move from request to request in increasing order of start time, we can use resources that are free but that have already been used by 38 | previous jobs, and if none are available we will have to use another available resource from _R_. This will split our resources up into three categories: 39 | ones that are in use (occupied), ones that have been used but are now free (released), and ones that have not been used and are free (R). If possible 40 | we would like to select our resource from released. 41 | 42 | ### Pseudo Code 43 | 44 | ```Python 45 | 46 | def interval_partitioning(set J: jobs): 47 | released = [] 48 | occupied = [] 49 | resources = [] # jobs being served by resource r where r is the index 50 | r = 0 # resource pointer 51 | 1. Sort J by starting time 52 | 2. For every j in J: 53 | a. move all resources in occupied that finished before the start of j into released 54 | b. if released is not empty: 55 | i. r = get resource from released 56 | c. else 57 | i. r = next resource in R 58 | d. move r into occupied 59 | e. put j into R[r] 60 | 3. return R 61 | 62 | ``` 63 | 64 | 65 | 66 | ### Analysis 67 | 68 | Interval partitioning - much like interval scheduling - will have a time complexity dependent on the sorting algorithm used within it. In our implementation, 69 | quicksort has a best and average time complexity of O(n log n) and a worst case of O(n2). All that is done in interval partitioning after 70 | the pre-sort is a loop n times through the set of jobs, placing each job in the list R at index of what resource it is assigned to. This means that - even in edge 71 | cases where each job overlaps all preceding jobs - our interval partitioning time complexity is equivalent to that of the sorting algorithm used. 72 | A faster sorting algorithm such as Radix sort which has a time complexity of O(kn) where k is the number of requests to be sorted, or 73 | counting sort which is O(N + k) where k is the range of the requests to be sorted, would both cause Version 2 to have a time complexity of O(n). 74 | 75 | ## Example 76 | 77 | **1. Jobs to be scheduled** 78 | 79 | 5 jobs are to be spread among as few resources as possible. 80 | 81 | ![Step 1](assets/step1.PNG "Jobs to be scheduled") 82 | 83 | **2. Jobs sorted by start time** 84 | 85 | Jobs are sorted by start time using quicksort 86 | 87 | ![Step 1](assets/step2.PNG "Jobs to be scheduled") 88 | 89 | **3. First job scheduled** 90 | 91 | The first job is assigned to resource 1 which is new. 92 | 93 | ![Step 1](assets/step3.PNG "First job scheduled") 94 | 95 | **4. Second job scheduled** 96 | 97 | The second job is assigned to new resource 2, due to resource 1 being occupied. 98 | 99 | ![Step 1](assets/step4.PNG "Second job scheduled") 100 | 101 | **4. Third job scheduled** 102 | 103 | The third job is assigned to new resource 3, due to resources 1 and 2 being occupied. 104 | 105 | ![Step 1](assets/step5.PNG "Third job scheduled") 106 | 107 | **5. First resource released** 108 | 109 | Before the start time of job 4, job 1 finishes using resource 1, and resource 1 is now released. 110 | 111 | ![Step 1](assets/step6.PNG "First resource released") 112 | 113 | **6. Fourth job scheduled** 114 | 115 | The fourth job is scheduled on the newly released resource 1. 116 | 117 | ![Step 1](assets/step7.PNG "Fourth job scheduled") 118 | 119 | 120 | **7. Second resource released** 121 | 122 | Before the start time of job 5, job 2 finishes using resource 2, and resource 2 is now released. 123 | 124 | 125 | ![Step 1](assets/step8.PNG "Second resource released") 126 | 127 | **6. Fifth job scheduled** 128 | 129 | The fifth job is scheduled on the newly released resource 2. 130 | 131 | ![Step 1](assets/step9.PNG "Fifth job scheduled") 132 | 133 | **Done** 134 | 135 | ## Conclusion 136 | The interval partitioning algorithm is greedy and yet yields an optimal solution by pre-sorting jobs by their start time. 137 | -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Interval Partitioning/assets/step1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Greedy/Educational/Interval Partitioning/assets/step1.PNG -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Interval Partitioning/assets/step2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Greedy/Educational/Interval Partitioning/assets/step2.PNG -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Interval Partitioning/assets/step3.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Greedy/Educational/Interval Partitioning/assets/step3.PNG -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Interval Partitioning/assets/step4.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Greedy/Educational/Interval Partitioning/assets/step4.PNG -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Interval Partitioning/assets/step5.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Greedy/Educational/Interval Partitioning/assets/step5.PNG -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Interval Partitioning/assets/step6.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Greedy/Educational/Interval Partitioning/assets/step6.PNG -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Interval Partitioning/assets/step7.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Greedy/Educational/Interval Partitioning/assets/step7.PNG -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Interval Partitioning/assets/step8.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Greedy/Educational/Interval Partitioning/assets/step8.PNG -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Interval Partitioning/assets/step9.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Greedy/Educational/Interval Partitioning/assets/step9.PNG -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Interval Partitioning/solution.py: -------------------------------------------------------------------------------- 1 | def partition(s, begin, end): 2 | """ 3 | Partially sort an array using a pivot value. Everything to the 4 | left of the pivot will be less than the pivot. Everything to the right 5 | will be greater than the pivot. 6 | :param s: the array to sort 7 | :param begin: the beginning of the section of the array to sort 8 | :param end: the end of the section of the array to sort 9 | :return: the array with sorted section begin-end 10 | """ 11 | pivot = end # set the pivot to the end of the array 12 | pivot_holder_index = begin # place holder for where the pivot will end up 13 | for i in range(begin, end): 14 | if s[i][0] < s[pivot][0]: # if s[i] < s[pivot] 15 | # switch s[i] and pivot place holder 16 | s[i], s[pivot_holder_index] = s[pivot_holder_index], s[i] 17 | pivot_holder_index += 1 # increase pivot place holder 18 | 19 | # finally, put pivot in its placeholder 20 | s[pivot_holder_index], s[end] = s[end], s[pivot_holder_index] 21 | return pivot_holder_index 22 | 23 | 24 | def quick_sort(s, begin, end): 25 | """ 26 | Quick sort 27 | :param s: the array to sort 28 | :param begin: the beginning of the array 29 | :param end: the ending of the array 30 | :return: the sorted array 31 | """ 32 | # Quick sort 33 | if begin < end: 34 | pivot = partition(s, begin, end) # find a pivot place 35 | quick_sort(s, begin, pivot - 1) # recursive sort sub array on the left 36 | quick_sort(s, pivot + 1, end) # recursive sort sub array on the right 37 | return s 38 | 39 | 40 | def adjust(start, released, occupied, r): 41 | """ 42 | Adjust the status of resources based on if they are being used, 43 | have been used, or have not been used. 44 | :param start: the start time of the next job 45 | :param released: the resources that have been used but are free 46 | :param occupied: the resources that are currently being used 47 | :param r: the resources and their scheduled jobs 48 | :return: the list of released jobs and the list of occupied jobs 49 | """ 50 | 51 | if r: # if there are/have been resources in use 52 | for i in occupied: # check if any in occupied are now done 53 | if r[i][-1][-1] < start: 54 | released.append(i) # move from occupied -> released 55 | occupied.remove(i) 56 | return released, occupied # return the set of resources and their stats 57 | else: 58 | # no resources have been used 59 | return released, occupied 60 | 61 | 62 | def interval_partition(set_of_jobs): 63 | """ 64 | Given a set of jobs and assuming an infinite amount of resources, 65 | schedule all jobs using as few resources as possible 66 | :param set_of_jobs: an array of tuples representing a job with (s, f) 67 | :return: the resources and their scheduled jobs 68 | """ 69 | released = [] # holds index of resources in r that are free 70 | occupied = [] # holds index of resources in r that are occupied 71 | resources = [] # holds all resources as lists of jobs that they have 72 | 73 | quick_sort(set_of_jobs, 0, len(set_of_jobs) -1) # sort jobs by start time 74 | for j in set_of_jobs: 75 | # move all free resources in occupied to released 76 | released, occupied = adjust(j[0], released, occupied, resources) 77 | if released: 78 | r = released[0] # choose a resource that is free 79 | released.remove(r) # resource is no longer free 80 | else: 81 | r = len(resources) 82 | resources.append([]) # create a new resource to assign jobs to 83 | occupied.append(r) # move the resource into occupied 84 | resources[r].append(j) # assign this job to the resource 85 | return resources 86 | 87 | 88 | def main(): 89 | jobs = [(1, 4), (3, 8), (5, 7), (7, 9), (2, 6)] 90 | schedule = interval_partition(jobs) 91 | 92 | # pretty print 93 | for resource, jobs in enumerate(schedule): 94 | print('Resource {}:'.format(resource)) 95 | for job in jobs: 96 | print('\tJob {}'.format(job)) 97 | 98 | if __name__ == '__main__': 99 | main() -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Interval Scheduling/README.md: -------------------------------------------------------------------------------- 1 | # Interval Scheduling 2 | 3 | This problem is discussed in [Dr. Gelfond Applied Algorithms](http://redwood.cs.ttu.edu/~mgelfond/FALL-2012/slides.pdf) and in [Wikipedia](https://en.wikipedia.org/wiki/Interval_scheduling) 4 | 5 | 6 | Category: Greedy 7 | 8 | Difficulty: Medium 9 | 10 | ## Problem 11 | ### Overview 12 | There is one resource which can be used by at most one person at a time. Multiple people request the use of this resource with a start time _s_ and a finish time _f_. Maximize the number of accepted requests so that no request (s, f) overlap, and the set of accepted requests are optimal, i.e. there is no other compatible set of requests with more requests. 13 | 14 | _From Wikipedia:_ 15 | > Interval schedluing is a class of problems in computer science, particularly in the area of algorithm design. The problems consider a set of tasks. Each task is represented by an _interval_ describing the time in which it needs to be executed. For instance, task A might run from 2:00 to 5:00, task B might run from 4:00 to 10:00, and task C might run from 9:00 to 11:00. A subset of intervals is _compatible_ if no two intervals overlap. For example, the subset {A, C} is compatible, as is the subset {B}; but neither {A, B} or {B, C} are compatible subsets, because the intervals within each subset overlap. 16 | 17 | ### Input Format 18 | - A set _S_ containing n rquests in the form of _(s, f)_ where _s_ is the start time and _f_ is the finish time of a request. 19 | 20 | ### Constraints 21 | - No two intervals _(s, f)_ may overlap within a solution, and the solution must be the most optimal soltuion of _S_, i.e. maximum number of requests in the solution. 22 | 23 | ### Output Format 24 | - a list of the intervals existing in the optimal solution. 25 | 26 | ## Algorithm 27 | ### Overview 28 | For our interval scheduling algorithm we will re-state the problem in a more precise way: 29 | 30 | > Given a set _R_ = {i1...,in} of requests where each request _i_ is associated with an interval [s(i), f(i)), find a best schedule, i.e. a subset _A_ of requests from _R_ such that: 31 | 32 | > 1. _A_ is _compatible_, i.e. _A_ contains no overlapping requests and 33 | 34 | > 2. _A_ is _optimal_, i.e. there is no compatible subset of _R_ which has more requests than _A_. 35 | 36 | 37 | ### Pseudo Code 38 | 39 | ## Version 1 40 | 41 | ```python 42 | 43 | def interval_scheduling(set_of_requests): 44 | 1. While set_of_requests is not empty 45 | a. select a request x from set_of_requests with earliest finish time 46 | b. add x to the solution set 47 | c. remove all sets incompatible with x from set_of_requests including x 48 | 2. return the solution set 49 | ``` 50 | 51 | ## Version 2 52 | 53 | ```python 54 | def interval_scheduling(set_of_requests): 55 | 1. Sort set_of_requests in order of finishing time. 56 | 2. i = 0, n = len(set_of_requests) 57 | 3. f := − 1 58 | 4. while i <= n: 59 | a. if set_of_requests[i]'s finish time is greater than or equal to f: 60 | 1. append the request to the solution set 61 | 2. f = the requests finish time 62 | b. i = i + 1 63 | 5. return the solution set 64 | 65 | ``` 66 | 67 | ### Analysis 68 | 69 | ## Version 1 70 | 71 | Version 1 of the solution has a worst case time complexity of O(n2) and a best case of O(n). Since the list of requests is not sorted 72 | the algorithm must loop over the list of requests n times for the outer while loop, and n more times for finding the minimal finish time. 73 | 74 | ## Example 75 | 76 | Requests = [(6, 9), (2, 3), (1, 4), (1, 3), (4, 5)] 77 | 78 | Solution = [] 79 | 80 | Step 1: 81 | 82 | a. first request with minimum finish time is (2, 3) 83 | 84 | b. Solution = [(2, 3)] 85 | 86 | c. Requests = [(6, 9), (4, 5)] 87 | 88 | Step 2: 89 | 90 | a. request with minimum finish time is (4, 5) 91 | 92 | b. Solution = [(2, 3), (4, 5)] 93 | 94 | c. Requests = [(6, 9)] 95 | 96 | Step 3: 97 | 98 | a. request with minimum finsih time is (6, 9) 99 | 100 | b. Solution = [(2, 3), (4, 5), (6, 9)] 101 | 102 | c. Request = [] 103 | 104 | Solution set = [(2, 3), (4, 5), (6, 9)] 105 | 106 | ## Version 2 107 | 108 | Version 2 of the solution has a worst case time complexity of O(n2) and an average time complexity of O(n log n) from quick sorting the set of requests before finding the optimal solution. 109 | The time complexity of the algorithm when the entire set of requests is sorted by finish time is simply n, and the time complexity of sorting 110 | the un-sorted set of requests is O(n log n), which means T(n) = O(n log n) + O(n) wherein the extra addition of n can be dropped. 111 | A faster sorting algorithm such as Radix sort which has a time complexity of O(kn) where k is the number of requests to be sorted, or 112 | counting sort which is O(N + k) where k is the range of the requests to be sorted, would both cause Version 2 to have a time complexity of O(n). 113 | 114 | To be precise, radix sort is O(kN), where k is the number of digits in the values to be sorted. 115 | Counting sort is O(N + k), where k is the range of the numbers to be sorted. 116 | 117 | ## Example 118 | 119 | Requests = [(6, 9), (2, 3), (1, 4), (1, 3), (4, 5)] 120 | Solution = [] 121 | 122 | 1. Sort requests by finish time. Requests = [(1, 3), (2, 3), (1, 4), (4, 5), (6, 9)] 123 | 124 | First Iteration: 125 | 126 | request = (1, 3) 127 | 128 | Since the previous finish time (-1) is greater than this requests start time (1), 129 | the request is added to the solution set and the new finsh time is 3. 130 | 131 | solution set = [(1, 3)] 132 | 133 | Second Iteration: 134 | 135 | request = (2, 3) 136 | 137 | Since the previous finsh time (3) is greater than this requests start time (2), 138 | the request is not added to the solution set and the finish time is still 3. 139 | 140 | solution set = [(1, 3)] 141 | 142 | Third Iteration: 143 | 144 | request = (1, 4) 145 | 146 | Since the previous request finish time (3) is greater than this requests start time (1), 147 | the request is not added to the solution set and the finish time is still 3. 148 | 149 | soltion set = [(1, 3)] 150 | 151 | Fourth Iteration: 152 | 153 | request = (4, 5) 154 | 155 | Since the previous request finish time (3) is less than this requests start time (4), 156 | the request is added to the solution set and the finish time is now 5. 157 | 158 | solution set = [(1, 3), (4, 5)] 159 | 160 | Fifth Iteration: 161 | 162 | request = (6, 9) 163 | 164 | Since the previous request finish time (5) is less than this requests start time (6), 165 | the request is added to the solution set and the finish time is now 9. 166 | 167 | solution set = [(1, 3), (4, 5), (6, 9)] 168 | 169 | We have completed looping over the entire sorted set of requests and placed compatible ones in the solution set. 170 | Final Solution Set = [(1, 3), (4, 5), (6, 9)] 171 | 172 | 173 | ## Conclusion 174 | 175 | For the conclusion we will view a short piece from the Wiki (modified slightly) on Interval Scheduling (discussing version 1 of the algorithm): 176 | 177 | > Whenever we select an interval at step a, we may have to remove many intervals in step c. 178 | > However, all these intervals necessarily cross the finishing time of x, and thus they all cross each other. 179 | > Hence, at most 1 of these intervals can be in the optimal solution. 180 | > Hence, for every interval in the optimal solution, there is an interval in the greedy solution. 181 | > This proves that the greedy algorithm indeed finds an optimal solution. 182 | -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Interval Scheduling/solutionV1.py: -------------------------------------------------------------------------------- 1 | def remove_invalid(s, request): 2 | """ 3 | Remove all conflicting intervals from rthe set of requests 4 | :param s: the set of requests 5 | :param request_index: the index of the request to check for conflicts with 6 | :return: the resulting requests 7 | """ 8 | result_set = [] # the set of non-conflicting requests 9 | for r in s: 10 | if r[0] >= request[1]: # if the start of r is after request finish 11 | result_set.append(r) # add to the solution set 12 | return result_set 13 | 14 | 15 | def get_min_finish_time(s): 16 | """ 17 | Get the request with the minimum finish time in s 18 | :param s: the set of requests 19 | :return: the request with the minimum finish time 20 | """ 21 | min_request = s[0] 22 | for r in s: 23 | if r[1] < min_request[1]: 24 | min_request = r # reassign min 25 | 26 | return min_request 27 | 28 | 29 | def interval_schedule(set_of_intervals): 30 | """ 31 | Schedule a set of intervals so that the most requests get scheduled, there 32 | are no overlapping requests, and the schedule is optimal 33 | :param set_of_intervals: the set of requests 34 | :return: an optimal schedule 35 | """ 36 | solution = [] 37 | while set_of_intervals: 38 | request = get_min_finish_time(set_of_intervals) # get r with min finish 39 | solution.append(request) # add r to the solution 40 | # remove incompatible requests 41 | set_of_intervals = remove_invalid(set_of_intervals, request) 42 | return solution 43 | 44 | 45 | def main(): 46 | 47 | set_of_intervals = [(6, 9), (2, 3), (1, 4), (1, 3), (4, 5)] 48 | print(interval_schedule(set_of_intervals)) 49 | 50 | 51 | if __name__ == '__main__': 52 | main() -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Interval Scheduling/solutionV2.py: -------------------------------------------------------------------------------- 1 | def partition(s, begin, end): 2 | """ 3 | Partially sort an array using a pivot value. Everything to the 4 | left of the pivot will be less than the pivot. Everything to the right 5 | will be greater than the pivot. 6 | :param s: the array to sort 7 | :param begin: the beginning of the section of the array to sort 8 | :param end: the end of the section of the array to sort 9 | :return: the array with sorted section begin-end 10 | """ 11 | pivot = end # set the pivot to the end of the array 12 | pivot_holder_index = begin # place holder for where the pivot will end up 13 | for i in range(begin, end): 14 | if s[i][1] < s[pivot][1]: # if s[i] < s[pivot] 15 | # switch s[i] and pivot place holder 16 | s[i], s[pivot_holder_index] = s[pivot_holder_index], s[i] 17 | pivot_holder_index += 1 # increase pivot place holder 18 | 19 | # finally, put pivot in its placeholder 20 | s[pivot_holder_index], s[end] = s[end], s[pivot_holder_index] 21 | return pivot_holder_index 22 | 23 | 24 | def quick_sort(s, begin, end): 25 | """ 26 | Quick sort 27 | :param s: the array to sort 28 | :param begin: the beginning of the array 29 | :param end: the ending of the array 30 | :return: the sorted array 31 | """ 32 | # Quick sort 33 | if begin < end: 34 | pivot = partition(s, begin, end) # find a pivot place 35 | quick_sort(s, begin, pivot - 1) # recursive sort sub array on the left 36 | quick_sort(s, pivot + 1, end) # recursive sort sub array on the right 37 | return s 38 | 39 | 40 | def interval_schedule(set_of_intervals): 41 | """ 42 | Schedule a set of intervals so that the most requests get scheduled, there 43 | are no overlapping requests, and the schedule is optimal 44 | :param set_of_intervals: the set of requests 45 | :return: an optimal schedule 46 | """ 47 | # Order the intervals by finish time 48 | requests = quick_sort(set_of_intervals, 0, len(set_of_intervals) - 1) 49 | 50 | last_finish = -1 # initialize the last finish time 51 | schedule = [] # the resulting schedule 52 | for r in requests: 53 | if r[0] >= last_finish: 54 | schedule.append(r) # add to solution 55 | last_finish = r[1] # update last finish time to this requests 56 | 57 | return schedule 58 | 59 | 60 | def main(): 61 | 62 | set_of_intervals = [(6, 9), (2, 3), (1, 4), (1, 3), (4, 5)] 63 | print(interval_schedule(set_of_intervals)) 64 | 65 | 66 | if __name__ == '__main__': 67 | main() -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Minimizing Maximum Lateness Scheduling/README.md: -------------------------------------------------------------------------------- 1 | # Minimize Maximum Lateness Scheduling 2 | 3 | This problem is discussed in [Dr. Gelfond Applied Algorithms](http://redwood.cs.ttu.edu/~mgelfond/FALL-2012/slides.pdf) 4 | 5 | Category: Greedy 6 | 7 | Difficulty: N/A 8 | 9 | ## Problem 10 | ### Overview 11 | Given a set _R_ of requests where each request _i_ has a duration _ti_ and a deadline 12 | _di_, and a single unlimited resource, schedule each request _i_ into an interval with 13 | a start time _s(i)_ and a finish time _f(i)_ i.e. _[s(i), f(i))_ with length _ti_ such 14 | that each interval is disjoint. 15 | 16 | A request _i_ is late if _f(i) > di_. A requests lateness _Li_ is calculated by 17 | _f(i) - di_ if _i_ is late and 0 otherwise. 18 | 19 | Find a schedule of all requests which: 20 | - Starts at a given point _s_ and, 21 | - Minimizes maximum lateness, L = maxi(Li) 22 | 23 | 24 | ### Input Format 25 | - The set R of requests, each having a duration and a deadline in the form of _(ti, di)_. 26 | ### Constraints 27 | 28 | 29 | ### Output Format 30 | - A list of the requests with their start time and finish time in the form _(si, fi)_ 31 | 32 | ## Algorithm 33 | ### Overview 34 | To minimize the lateness that the jobs run, it makes sense to schedule the ones with the earliest deadlines first. 35 | 36 | ### Pseudo Code 37 | 38 | ```Python 39 | def min_max_lateness(R: set of requests): 40 | 1. Sort request by their deadline (earliest first) 41 | 2. f = 0 # initialize 'Finish' to 0 42 | 3. For every request i in R: 43 | a. start of i = f 44 | b. finish of i = start of i + time i requires to finish 45 | c. f = f + time i requires to finish 46 | 4. Return [s(i), f(i)] for every i 47 | ``` 48 | 49 | ### Analysis 50 | This algorithm is much like interval scheduling and interval partitioning. The time complexity is largely influenced by that of the 51 | sorting algorithm used. In this case we will use quick sort, which has an average time complexity of O(nlogn), and a worst case of O(n2). 52 | This will give our scheduling an average time complexity of O(nlogn). 53 | 54 | 55 | ### Example 56 | 57 | Initial set of requests: (3, 10), (4, 5), (1, 6), (6, 2), (1, 11) 58 | 59 | Step 1 - Sort Requests by deadline 60 | Sorted Requests = (6, 2), (4, 5), (1, 6), (3, 10), (1, 11) 61 | 62 | Step 2 - Initialize variable to hold last finish time 63 | f = 0 64 | 65 | Step 2 - Loop through sorted requests and calculate start and finish time 66 | 67 | Iteration 1, request (6, 2): 68 | * starti = 0 69 | * finishi = starti + 6 70 | * f = f + 6 71 | * schedule for this request: [(0, 6)] 72 | 73 | Iteration 2, request (4, 5): 74 | * starti = 6 75 | * finishi = starti + 4 76 | * f = f + 4 77 | * schedule for this request: [(6, 10)] 78 | 79 | Iteration 3, request (1, 6): 80 | * starti = 10 81 | * finishi = starti + 1 82 | * f = f + 1 83 | * schedule for this request: [(10, 11)] 84 | 85 | Iteration 4, request (3, 10): 86 | * starti = 11 87 | * finishi = starti + 3 88 | * f = f + 3 89 | * schedule for this request: [(11, 14)] 90 | 91 | Iteration 5, request (1, 11): 92 | * starti = 14 93 | * finishi = starti + 1 94 | * f = f + 1 95 | * schedule for this request: [(14, 15)] 96 | 97 | Done. The completed schedule is: 98 | 99 | | Request (ti, di) | (Start, Finish) | 100 | |----|--------------| 101 | | (6, 2) | (0, 6) | 102 | | (4, 5) | (6, 10) | 103 | | (1, 6) | (10, 11) | 104 | | (3, 10) | (11, 14) | 105 | | (1, 11) | (14, 15) | 106 | 107 | 108 | ## Conclusion 109 | The schedules are now sorted and each have a start and finish time that will minimize their lateness (finish time - deadline). 110 | 111 | -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Minimizing Maximum Lateness Scheduling/solution.py: -------------------------------------------------------------------------------- 1 | def partition(s, begin, end): 2 | """ 3 | Partially sort an array using a pivot value. Everything to the 4 | left of the pivot will be less than the pivot. Everything to the right 5 | will be greater than the pivot. 6 | :param s: the array to sort 7 | :param begin: the beginning of the section of the array to sort 8 | :param end: the end of the section of the array to sort 9 | :return: the array with sorted section begin-end 10 | """ 11 | pivot = end # set the pivot to the end of the array 12 | pivot_holder_index = begin # place holder for where the pivot will end up 13 | for i in range(begin, end): 14 | if s[i][1] < s[pivot][1]: # if s[i] < s[pivot] 15 | # switch s[i] and pivot place holder 16 | s[i], s[pivot_holder_index] = s[pivot_holder_index], s[i] 17 | pivot_holder_index += 1 # increase pivot place holder 18 | 19 | # finally, put pivot in its placeholder 20 | s[pivot_holder_index], s[end] = s[end], s[pivot_holder_index] 21 | return pivot_holder_index 22 | 23 | 24 | def quick_sort(s, begin, end): 25 | """ 26 | Quick sort 27 | :param s: the array to sort 28 | :param begin: the beginning of the array 29 | :param end: the ending of the array 30 | :return: the sorted array 31 | """ 32 | # Quick sort 33 | if begin < end: 34 | pivot = partition(s, begin, end) # find a pivot place 35 | quick_sort(s, begin, pivot - 1) # recursive sort sub array on the left 36 | quick_sort(s, pivot + 1, end) # recursive sort sub array on the right 37 | return s 38 | 39 | 40 | def minimize_lateness(set_of_requests): 41 | """ 42 | Make a schedule for a set of requests so that the lateness (the time the request goes 43 | over it's deadline) is minimized. 44 | :param set_of_requests: the set of requests to build a schedule for 45 | :return: the schedule for all of the requests 46 | """ 47 | 48 | # Sort request by deadline in ascending order 49 | sorted_requests = quick_sort(set_of_requests, 0, len(set_of_requests)-1) 50 | last_finish_time = 0 # initial finish time 51 | schedule = [] # initial schedule for requests 52 | for i in sorted_requests: 53 | start_time = last_finish_time # start of requests is last finish time 54 | finish_time = start_time + i[0] # this requests finish time 55 | last_finish_time += i[0] # update latest finish time 56 | lateness = finish_time - i[1] # calculate this requests lateness 57 | schedule.append([i,(start_time, finish_time), lateness]) 58 | return schedule 59 | 60 | 61 | def main(): 62 | # Request: (time, deadline) 63 | requests = [(3, 10), (4, 5), (1, 6), (6, 2), (1, 11)] 64 | scheduled = minimize_lateness(requests) 65 | 66 | # Pretty print 67 | print('Request\tStart\tFinish\tLateness') 68 | for i in scheduled: 69 | print('{}\t{}\t\t{}\t\t{}'.format(i[0], i[1][0], i[1][1], i[2])) 70 | 71 | 72 | if __name__ == '__main__': 73 | main() -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Minimum Spanning Tree/DisjointSet.py: -------------------------------------------------------------------------------- 1 | class DisjointSet(object): 2 | 3 | class Node(object): 4 | def __init__(self, node_id, rank=0): 5 | self.id = node_id 6 | self.parent = self 7 | self.rank = rank 8 | 9 | __disjoint_set_dict = dict() 10 | 11 | def make_set(self, x): 12 | """ 13 | Add a new set disjoint from all others to the list 14 | :param x: the id of the set to add 15 | :return: the new node or None if the node already exists in the set 16 | """ 17 | node = None 18 | if x not in self.__disjoint_set_dict.keys(): 19 | node = self.Node(x) 20 | self.__disjoint_set_dict[x] = node 21 | return node 22 | 23 | def make_sets(self, array): 24 | """ 25 | Add new disjoint sets for each id in the array 26 | :param array: list of id's to make sets out of 27 | :return: the new list of disjoint sets 28 | """ 29 | new_sets = [] 30 | for x in array: 31 | if x not in self.__disjoint_set_dict.keys(): 32 | node = self.Node(x) 33 | new_sets.append(node) 34 | self.__disjoint_set_dict[x] = node 35 | return new_sets 36 | 37 | 38 | def find(self, x): 39 | """ 40 | Find the root node of a set with id x in it 41 | :param x: the id of the node to find the root of 42 | :return: the root node 43 | """ 44 | node = self.__disjoint_set_dict[x] # get node representation of x 45 | if node.parent != node: 46 | node.parent = self.find(node.parent.id) 47 | return node.parent 48 | 49 | def union(self, x, y): 50 | """ 51 | Union two sets together, preferably smaller to bigger 52 | :param x: id of the first node 53 | :param y: id of the second node 54 | """ 55 | 56 | x_root = self.find(x) 57 | y_root = self.find(y) 58 | 59 | if x_root == y_root: 60 | return 61 | 62 | if x_root.rank < y_root.rank: # if y is bigger 63 | x_root.parent = y_root 64 | elif x_root.rank > y_root.rank: # if x is bigger 65 | y_root.parent = x_root 66 | else: 67 | y_root.parent = x_root 68 | x_root.rank += 1 69 | 70 | def print(self): 71 | """ 72 | Print each disjoint set and it's connections 73 | """ 74 | for k, v in self.__disjoint_set_dict.items(): 75 | print(k, end='') 76 | while v.parent != v: 77 | print('-> {}'.format(v.parent.id), end='') 78 | v = v.parent 79 | print('') 80 | 81 | 82 | def main(): 83 | d = DisjointSet() 84 | d.make_set('a') 85 | d.make_set('b') 86 | d.make_set('c') 87 | d.union('a', 'b') 88 | d.union('b', 'c') 89 | d.print() 90 | 91 | 92 | 93 | 94 | if __name__ == '__main__': 95 | main() -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Minimum Spanning Tree/Graph.py: -------------------------------------------------------------------------------- 1 | class Graph(object): 2 | """ 3 | Simple dict representation of a weighted graph that can be 4 | directed or undirected. 5 | """ 6 | 7 | def __init__(self, connections, directed=False): 8 | self.__graph = dict() # vertex : [(vertex, distance),...] 9 | self.__directed = directed 10 | self.edges = connections 11 | self.add_connections(connections) # initialize graph 12 | 13 | def add_vertex(self, v): 14 | """ 15 | add a vertex to the graph dictionary if it doesnt exist 16 | :param v: id of vertex to add 17 | """ 18 | existing = list(self.__graph.keys()) 19 | if v not in existing: 20 | self.__graph[v] = [] 21 | 22 | def add_connections(self, connections): 23 | """ 24 | add list of tuple pairs to graph 25 | :param connections: list of tuples in form (vertexa, vertexb, distance) 26 | """ 27 | for vertex1, vertex2, distance in connections: 28 | self.add_vertex(vertex1) 29 | self.add_vertex(vertex2) 30 | 31 | self.__graph[vertex1].append((vertex2, distance)) 32 | if not self.__directed: 33 | self.__graph[vertex2].append((vertex1, distance)) 34 | 35 | def get_neighbors(self, vertex): 36 | """ 37 | get all neighboring nodes of a vertex 38 | :param vertex: the id of vertex to get neighbors from 39 | :return: list of neighboring vertices by id 40 | """ 41 | return [x[0] for x in self.__graph[vertex]] 42 | 43 | def get_distance(self, vertex1, vertex2): 44 | """ 45 | get distance between two vertices in a graph 46 | NOTE: for directed graphs, vertex 2 MUST be a neighbor of vertex 1. 47 | :param vertex1: first vertex 48 | :param vertex2: second vertex 49 | :return: integer distance 50 | """ 51 | return [x[1] for x in self.__graph[vertex1] if x[0] == vertex2][0] 52 | 53 | def get_vertices(self): 54 | """ 55 | Get all vertices in a graph 56 | :return: list of all vertices in a graph by id 57 | """ 58 | return list(self.__graph.keys()) 59 | 60 | def get_edges(self): 61 | """ 62 | Get a list of all edges (connections) in the graph 63 | :return: 64 | """ 65 | return self.edges 66 | 67 | 68 | 69 | def main(): 70 | g = Graph([ 71 | ('A', 'B', 5), 72 | ('A', 'C', 2), 73 | ('B', 'C', 1), 74 | ('A', 'D', 5), 75 | ('D', 'C', 1), 76 | ('C', 'E', 1) 77 | ], directed=True) 78 | 79 | print(g.get_neighbors('A')) 80 | print(g.get_vertices()) 81 | 82 | 83 | if __name__ == '__main__': 84 | main() -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Minimum Spanning Tree/README.md: -------------------------------------------------------------------------------- 1 | # Minimum Spanning Tree 2 | 3 | This problem is discussed in [Dr. Gelfond Applied Algorithms](http://redwood.cs.ttu.edu/~mgelfond/FALL-2012/slides.pdf) and in [Wikipedia](https://en.wikipedia.org/wiki/Minimum_spanning_tree) 4 | 5 | 6 | Category: Greedy 7 | 8 | Difficulty to Understand: Hard 9 | 10 | ## Problem 11 | Given a graph G = (V, E) with positive cost ce associated with every edge e and: 12 | * An undirected graph is a _tree_ if it is connected and has no cycles (a cycle must include at least two different edges), 13 | * _Spanning tree_ of G is a set T of edges such that (V, T) is a tree, 14 | * _Cost_ of T is the sum of costs of its edges, 15 | * _Minimum spanning tree_ of G is a spanning tree of G with minimal cost, 16 | find a minimum spanning tree of G. 17 | 18 | ### Overview 19 | There are many algorithms to find the minimum spanning tree of a graph G. The one we will be using is [Kruskal's Algorithm](https://en.wikipedia.org/wiki/Kruskal%27s_algorithm#Pseudocode). 20 | We will implement the most-optimized version of the algorithm in Dr. Gelfond's lecture slides using disjoint-set data structure with Union() and Find() functions. 21 | 22 | **[Disjoint-set data structure](https://en.wikipedia.org/wiki/Disjoint-set_data_structure#MakeSet)**: is a data structure that keeps track of a set of elements partitioned into a number of disjoint (non-overlapping) subsets. 23 | 24 | Here is a disjoint-set of 8 elements taken from the above Wikipedia page: 25 | 26 | ![Initial Disjoint Set](assets/disjoint_set_individual.png "8 individual disjoint sets") 27 | 28 | As we add edges (comprised of vertices) to our minimum spanning tree, we will also Union() the two vertices in the edge together. After numerous iterations of this, the 29 | above initial disjoint set would now look something like this: 30 | 31 | ![Disjoint Set Grouped](assets/disjoint_set_group.png "8 overlapping / partially grouped disjoint sets") 32 | 33 | The reason for doing this will be discussed further in the Analysis section of this guide. For a more in-depth discussion of disjoint-set data types and 34 | related topics, please visit the [Disjoint-set](https://en.wikipedia.org/wiki/Disjoint-set_data_structure#MakeSet) Wikipedia page. 35 | 36 | 37 | ### Input Format 38 | Our input format will a graph G using our custom Graph class, 39 | 40 | The graph data structure will just be a simple representation of a directed or undirected graph using key-value pairs in dictionaries. A graph is represented by a single dictionary with entries in the form: v : [(u, d), (u1, d),..., (un, d)] where the key v is the id of a vertex in the graph (i.e., 'A'), and the value (a list of tuples) contain u: a connected node and d: the distance from v to u. 41 | 42 | ### Constraints 43 | Due to our disjoint-set data structure being a crucial part to solving this problem, each vertex existing anywhere in our input graph G 44 | must also have a corresponding set in our DisjointSet data structure, so that we can perform `union()` and `find()` operations on them. 45 | 46 | ### Output Format 47 | Each edge that is included in the minimum spanning tree of our graph separated by a new line and shown in the form _u--w--v_ where _u_ and _v_ are vertices and w is the edge's weight. 48 | 49 | ## Algorithm 50 | ### Overview 51 | The basic idea to create a minimum spanning tree using Kruskal's algorithm is to sort all edges in increasing weight, and then 52 | loop through each edge checking to see if adding it to the solution would cause a cycle. 53 | 54 | By pre-sorting all edges, we guarantee that we are looking at the minimum weight paths first, and by checking to see if the edge would create a cycle 55 | before adding it to our minimum spanning tree, we are avoiding creating just another graph. 56 | 57 | ### Pseudo Code 58 | 59 | ```python 60 | 61 | def minimum_spanning_tree(G): 62 | solution = [] 63 | 1. sort all edges in G by weight 64 | 2. For e in sorted edges: 65 | a. if solution unioned with e causes no cycles: 66 | i. add e to solution 67 | 3. return solution 68 | ``` 69 | 70 | 71 | ### Analysis 72 | Kruskal's algorithm to find a minimum spanning tree utilizes the union-find disjoint-set data structure to avoid adding vertices to the minimum spanning tree 73 | that would cause a cycle, and therefore would disqualify the solution (it would no longer be a tree). 74 | 75 | The steps are: 76 | ``` 77 | 1. Sort all edges in G by weight 78 | 2. For each edge (u, w, v) 79 | a. if u is not in the same disjoint-set as v 80 | i. combine u and v's disjoint-sets 81 | ii. add (u, w, v) to the solution set 82 | ``` 83 | 84 | Step 1 is done using a comparison sort - in our case quick sort. 85 | Step 2a is done by using the Find() function on both vertices. Because each vertice is represented as a node with an id, rank, and 'pointer' to another parent node, 86 | each vertex that is in a set will be represented by the first vertex of that set. If _u_ and _v_ are in the same set, Find() will return the same parent vertex. 87 | Step 2a part i is done by calling the Union() function with both vertices. This function utilizes the rank to append the smaller of the two disjoint sets containing the vertices to the end of the larger one. 88 | Step 2a part ii is simply adding the current edge to the solution - our minimum spanning tree. 89 | 90 | Much like other greedy algorithms, Kruskal's algorithmic time complexity is largely determined by the pre-sort method used. In our case, quick sort would have 91 | an average case of O(E log E), where E is the number of edges in a graph G. 92 | 93 | If we use counting sort or radix sort to sort our edges by weight in linear time, the complexity decreases to O(E alpha(V)) time, where _alpha_ is the inverse of the 94 | [Ackermann Function](https://en.wikipedia.org/wiki/Ackermann_function). 95 | 96 | ## Example 97 | This excellent example is a slightly modified version from the Wikipedia page on Kruskal's algorithm. The images are unmodified. 98 | 99 | 1. Edge (A, 5, D): 100 | * Disjoint sets: [(A) (B) (C) (D) (E) (F) (G)] 101 | * Find(A) will return A, and Find(D) will return D, because initially all vertices are disjoint sets. A != D so, 102 | * The edge (A, D, 5) will be added to the solution set 103 | * The disjoint-set containing vertex A in it will be unioned with the disjoint set containing vertex D 104 | 105 | ![Step 1](assets/iter1.png "Examining the first edge") 106 | 107 | ---- 108 | 109 | 110 | 2. Edge C, E, 5): 111 | * Disjoint sets: [(A, D) (B) (C) (E) (F) (G)] 112 | * Find(C) will return C, and Find(E) will return E, and C != E so, 113 | * The edge (C, E, 5) will be added to the solution set 114 | * The disjoint-set containing vertex C in it will be unioned with the disjoint set containing vertex E 115 | 116 | ![Step 2](assets/iter2.png "Examining the second edge") 117 | 118 | ---- 119 | 120 | 3. Edge (D, F, 6): 121 | * Disjoint sets: [(A, D) (B) (C, E) (F) (G)] 122 | * Find(D) will return A because it is the parent of the set D is in, and Find(F) will return F, and A != F so, 123 | * The edge (D, F, 6) will be added to the solution set 124 | * The disjoint-set containing vertex D will be unioned with the disjoint set containing vertex F 125 | 126 | ![Step 3](assets/iter3.png "Examining the third edge") 127 | 128 | ---- 129 | 130 | 4. Edge (A, B, 7): 131 | * Disjoint sets: [(A, D, F) (B) (C, E) (G)] 132 | * Find(A) will return A, and Find(B) will return B, and A != B so, 133 | * The edge (A, B, 7) will be added to the solution set 134 | * The disjoint-set containing vertex A will be unioned with the disjoint set containing vertex B 135 | 136 | ![Step 4](assets/iter4.png "Examining the fourth edge") 137 | 138 | ---- 139 | 140 | 5. Edge (B, E, 7): 141 | * Disjoint sets: [(A, D, F, B) (C, E) (G)] 142 | * Find(B) will return A, and Find(E) will return E, and A != E so, 143 | * The edge (B, E, 7) will be added to the solution set 144 | * The disjoint-set containing vertex A will be unioned with the disjoint set containing vertex E 145 | 146 | 5a. Edge (B, C, 8): 147 | * Disjoint sets: [(A, D, F, B, C, E, C, E) (G)] 148 | * Find(B) will return A, and Find(E) will return A, and A == A, so 149 | * go to next edge 150 | 151 | 5a. Edge (F, E, 8): 152 | * Disjoint sets: [(A, D, F, B, C, E, C, E) (G)] 153 | * Find(F) will return A, and Find(E) will return A, and A == A, so 154 | * go to next edge 155 | 156 | 5b. Edge (B, C, 8): 157 | * Disjoint sets: [(A, D, F, B, C, E, C, E) (G)] 158 | * Find(B) will return A, and Find(E) will return A, and A == A, so 159 | * go to next edge 160 | 161 | ![Step 5](assets/iter5.png "Examining the fifth edge") 162 | 163 | ---- 164 | 165 | 166 | 6. Edge (E, G, 9): 167 | * Disjoint sets: [(A, D, F, B, C, E) (G)] 168 | * Find(E) will return A, and Find(G) will return G, and A != G so, 169 | * The edge (E, G, 9) will be added to the solution set 170 | * The disjoint-set containing vertex E will be unioned with the disjoint set containing vertex G 171 | 172 | ![Step 6](assets/iter6.png "Examining the sixth edge") 173 | 174 | 175 | NOTE: All edges after this point would only create a cycle if added to the solution, our Minimum Spanning tree is found. 176 | 177 | ---- 178 | 179 | ## Conclusion 180 | Our minimum spanning tree is found, and it is of minimal weight. For a further in-depth proof of Kruskal's algorithm, see [Proof of correctness](https://en.wikipedia.org/wiki/Kruskal%27s_algorithm#Proof_of_correctness) 181 | -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Minimum Spanning Tree/assets/MST_kruskal_en.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Greedy/Educational/Minimum Spanning Tree/assets/MST_kruskal_en.gif -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Minimum Spanning Tree/assets/disjoint_set_group.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Greedy/Educational/Minimum Spanning Tree/assets/disjoint_set_group.png -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Minimum Spanning Tree/assets/disjoint_set_individual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Greedy/Educational/Minimum Spanning Tree/assets/disjoint_set_individual.png -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Minimum Spanning Tree/assets/iter1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Greedy/Educational/Minimum Spanning Tree/assets/iter1.png -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Minimum Spanning Tree/assets/iter2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Greedy/Educational/Minimum Spanning Tree/assets/iter2.png -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Minimum Spanning Tree/assets/iter3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Greedy/Educational/Minimum Spanning Tree/assets/iter3.png -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Minimum Spanning Tree/assets/iter4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Greedy/Educational/Minimum Spanning Tree/assets/iter4.png -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Minimum Spanning Tree/assets/iter5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Greedy/Educational/Minimum Spanning Tree/assets/iter5.png -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Minimum Spanning Tree/assets/iter6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Greedy/Educational/Minimum Spanning Tree/assets/iter6.png -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Minimum Spanning Tree/solution.py: -------------------------------------------------------------------------------- 1 | from Graph import Graph 2 | from DisjointSet import DisjointSet 3 | 4 | 5 | def partition(s, begin, end): 6 | """ 7 | Partially sort an array using a pivot value. Everything to the 8 | left of the pivot will be less than the pivot. Everything to the right 9 | will be greater than the pivot. 10 | :param s: the array to sort 11 | :param begin: the beginning of the section of the array to sort 12 | :param end: the end of the section of the array to sort 13 | :return: the array with sorted section begin-end 14 | """ 15 | pivot = end # set the pivot to the end of the array 16 | pivot_holder_index = begin # place holder for where the pivot will end up 17 | for i in range(begin, end): 18 | if s[i][2] < s[pivot][2]: # if s[i] < s[pivot] 19 | # switch s[i] and pivot place holder 20 | s[i], s[pivot_holder_index] = s[pivot_holder_index], s[i] 21 | pivot_holder_index += 1 # increase pivot place holder 22 | 23 | # finally, put pivot in its placeholder 24 | s[pivot_holder_index], s[end] = s[end], s[pivot_holder_index] 25 | return pivot_holder_index 26 | 27 | 28 | def quick_sort(s, begin, end): 29 | """ 30 | Quick sort 31 | :param s: the array to sort 32 | :param begin: the beginning of the array 33 | :param end: the ending of the array 34 | :return: the sorted array 35 | """ 36 | # Quick sort 37 | if begin < end: 38 | pivot = partition(s, begin, end) # find a pivot place 39 | quick_sort(s, begin, pivot - 1) # recursive sort sub array on the left 40 | quick_sort(s, pivot + 1, end) # recursive sort sub array on the right 41 | return s 42 | 43 | 44 | def minimum_spanning_tree(graph): 45 | """ 46 | Find the minimum spanning tree in the given graph using 47 | Kruskal's algorithm 48 | :param graph: the graph to find the MST in 49 | :return: the set of all edges in the MST 50 | """ 51 | d = DisjointSet() # initialize disjoint set data structure 52 | d.make_sets(graph.get_vertices()) 53 | edges = graph.get_edges() # All edges in graph 54 | solution = set() # Set of edges in MST 55 | quick_sort(edges, 0, len(edges)-1) # Sort by edge weight in asc 56 | 57 | for e in edges: 58 | if d.find(e[0]) != d.find(e[1]): # if the vertices wont make a cycle 59 | d.union(e[0], e[1]) # union them 60 | solution.add(e) # add the edge to the solution 61 | return solution 62 | 63 | 64 | def main(): 65 | 66 | graph = Graph([ 67 | ('a', 'b', 7), 68 | ('b', 'c', 8), 69 | ('b', 'd', 9), 70 | ('d', 'a', 5), 71 | ('b', 'e', 7), 72 | ('c', 'e', 5), 73 | ('e', 'd', 15), 74 | ('d', 'f', 6), 75 | ('f', 'g', 11), 76 | ('g', 'e', 9), 77 | ('f', 'e', 8) 78 | ], directed=False) 79 | 80 | mst = minimum_spanning_tree(graph) 81 | 82 | # pretty print all of the edges in the mst 83 | for e in mst: 84 | print('{}--{}--{}'.format(e[0], e[2], e[1])) 85 | 86 | 87 | if __name__ == '__main__': 88 | main() -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Shortest Path/Graph.py: -------------------------------------------------------------------------------- 1 | 2 | class Graph(object): 3 | """ 4 | Simple dict representation of a weighted graph that can be 5 | directed or undirected. 6 | """ 7 | 8 | def __init__(self, connections, directed=False): 9 | self.__graph = dict() # vertex : [(vertex, distance),...] 10 | self.__directed = directed 11 | self.add_connections(connections) # initialize graph 12 | 13 | def add_vertex(self, v): 14 | """ 15 | add a vertex to the graph dictionary if it doesnt exist 16 | :param v: id of vertex to add 17 | """ 18 | existing = list(self.__graph.keys()) 19 | if v not in existing: 20 | self.__graph[v] = [] 21 | 22 | def add_connections(self, connections): 23 | """ 24 | add list of tuple pairs to graph 25 | :param connections: list of tuples in form (vertexa, vertexb, distance) 26 | """ 27 | for vertex1, vertex2, distance in connections: 28 | self.add_vertex(vertex1) 29 | self.add_vertex(vertex2) 30 | 31 | self.__graph[vertex1].append((vertex2, distance)) 32 | if not self.__directed: 33 | self.__graph[vertex2].append((vertex1, distance)) 34 | 35 | def get_neighbors(self, vertex): 36 | """ 37 | get all neighboring nodes of a vertex 38 | :param vertex: the id of vertex to get neighbors from 39 | :return: list of neighboring vertices by id 40 | """ 41 | return [x[0] for x in self.__graph[vertex]] 42 | 43 | def get_distance(self, vertex1, vertex2): 44 | """ 45 | get distance between two vertices in a graph 46 | NOTE: for directed graphs, vertex 2 MUST be a neighbor of vertex 1. 47 | :param vertex1: first vertex 48 | :param vertex2: second vertex 49 | :return: integer distance 50 | """ 51 | return [x[1] for x in self.__graph[vertex1] if x[0] == vertex2][0] 52 | 53 | def get_vertices(self): 54 | """ 55 | Get all vertices in a graph 56 | :return: list of all vertices in a graph by id 57 | """ 58 | return list(self.__graph.keys()) 59 | 60 | 61 | def main(): 62 | g = Graph([ 63 | ('A', 'B', 5), 64 | ('A', 'C', 2), 65 | ('B', 'C', 1), 66 | ('A', 'D', 5), 67 | ('D', 'C', 1), 68 | ('C', 'E', 1) 69 | ], directed=True) 70 | 71 | print(g.get_neighbors('A')) 72 | print(g.get_vertices()) 73 | 74 | 75 | if __name__ == '__main__': 76 | main() -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Shortest Path/assets/DijkstraDemo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Greedy/Educational/Shortest Path/assets/DijkstraDemo.gif -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Shortest Path/assets/idg_complete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Greedy/Educational/Shortest Path/assets/idg_complete.png -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Shortest Path/assets/initial_directed_graph1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Greedy/Educational/Shortest Path/assets/initial_directed_graph1.png -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Shortest Path/assets/initial_undirected_graph1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Greedy/Educational/Shortest Path/assets/initial_undirected_graph1.png -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Shortest Path/assets/iug_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Greedy/Educational/Shortest Path/assets/iug_01.png -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Shortest Path/assets/iug_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Greedy/Educational/Shortest Path/assets/iug_1.png -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Shortest Path/assets/iug_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Greedy/Educational/Shortest Path/assets/iug_2.png -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Shortest Path/assets/iug_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Greedy/Educational/Shortest Path/assets/iug_3.png -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Shortest Path/assets/iug_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Greedy/Educational/Shortest Path/assets/iug_4.png -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Shortest Path/assets/iug_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Greedy/Educational/Shortest Path/assets/iug_5.png -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Shortest Path/assets/iug_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Greedy/Educational/Shortest Path/assets/iug_6.png -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Shortest Path/solution.py: -------------------------------------------------------------------------------- 1 | from math import inf 2 | from FibHeap import FibHeap 3 | from Graph import Graph 4 | 5 | 6 | def dijkstra_shortest_paths(graph, source): 7 | """ 8 | Find the shortest paths between nodes in a graph 9 | :param graph: graph 10 | :param source: the starting node 11 | :return: path and distances of resulting tree in graph 12 | """ 13 | dist = dict() # dict to keep track of distances from source node 14 | prev = dict() # dict to keep track of a node's previous node in its path 15 | nodes = dict() # dict keep track of relationship between vertices in 16 | # graph and nodes in FibHeap 17 | 18 | # Initialize source node 19 | dist[source] = 0 20 | prev[source] = None 21 | 22 | queue = FibHeap() # Initialize priority queue 23 | 24 | # fill and initialize everything else 25 | for v in graph.get_vertices(): 26 | if v is not source: 27 | dist[v] = inf # distance unknown, inf is infinity from math.py 28 | prev[v] = None # no path yet 29 | nodes[v] = queue.add_with_priority(v, dist[v]) 30 | 31 | while not queue.is_empty(): 32 | u = queue.extract_min() # extract the node with minimum distance 33 | for v in graph.get_neighbors(u.node_id): 34 | # for each neighbor, calculate the distance from u to it 35 | alt = dist[u.node_id] + graph.get_distance(u.node_id, v) 36 | if alt < dist[v]: # if the new distance is shorter than old 37 | dist[v] = alt # our distance to this node is shorter now 38 | prev[v] = u.node_id # the path we take is through this node 39 | queue.decrease_priority(nodes[v], alt) # decrease distance 40 | return dist, prev 41 | 42 | 43 | def main(): 44 | g = Graph([ 45 | ('A', 'B', 14), 46 | ('A', 'C', 7), 47 | ('A', 'D', 9), 48 | ('C', 'D', 10), 49 | ('D', 'B', 2), 50 | ('E', 'D', 11), 51 | ('C', 'E', 15), 52 | ('E', 'F', 6), 53 | ('B', 'F', 9) 54 | ], directed=True) 55 | 56 | dist, path = dijkstra_shortest_paths(g, 'A') 57 | print('Directed Graph:\n\tDistances: {}\n\t Path: {}'.format(dist, path)) 58 | 59 | g = Graph([ 60 | ('A', 'B', 14), 61 | ('A', 'C', 7), 62 | ('A', 'D', 9), 63 | ('C', 'D', 10), 64 | ('D', 'B', 2), 65 | ('E', 'D', 11), 66 | ('C', 'E', 15), 67 | ('E', 'F', 6), 68 | ('B', 'F', 9) 69 | ], directed=False) 70 | 71 | dist, path = dijkstra_shortest_paths(g, 'A') 72 | print('Undirected Graph:\n\tDistances: {}\n\t Path: {}'.format(dist, path)) 73 | 74 | 75 | if __name__ == '__main__': 76 | main() 77 | -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Stable Marriage/assets/step1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Greedy/Educational/Stable Marriage/assets/step1.PNG -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Stable Marriage/assets/step2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Greedy/Educational/Stable Marriage/assets/step2.PNG -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Stable Marriage/assets/step3.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Greedy/Educational/Stable Marriage/assets/step3.PNG -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Stable Marriage/assets/step4.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Greedy/Educational/Stable Marriage/assets/step4.PNG -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Stable Marriage/assets/step5.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Greedy/Educational/Stable Marriage/assets/step5.PNG -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Stable Marriage/assets/step6.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Greedy/Educational/Stable Marriage/assets/step6.PNG -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Stable Marriage/assets/step7.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Greedy/Educational/Stable Marriage/assets/step7.PNG -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Stable Marriage/assets/step8.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Greedy/Educational/Stable Marriage/assets/step8.PNG -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Stable Marriage/assets/step9.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeSpaceHQ/AppliedAlgorithms/2a410d1da15953e6b6e466601f6e837f73812809/Guide/Greedy/Educational/Stable Marriage/assets/step9.PNG -------------------------------------------------------------------------------- /Guide/Greedy/Educational/Stable Marriage/solution.py: -------------------------------------------------------------------------------- 1 | 2 | def stable_marriage(men, women): 3 | """ 4 | Given a set of men and women where both have ranked each person in the other 5 | set, find a mapping between the two sets such that each pair is stable. 6 | :param men: a dict of men with key, value where key is the id and value is 7 | a list id's representing women where their index is their rank 8 | :param women: a dict of women with key, value where key is the id and value is 9 | a list id's representing men where their index is their rank 10 | :return: a dict of all engaged pairs by wife 11 | """ 12 | single_men = list(men.keys()) # Initial list of single men 13 | wives = dict() # Initial mapping of women to men 14 | 15 | while single_men: 16 | man = single_men[0] # get a single man 17 | 18 | preferred_wife = men[man][0] # get the man's highest ranked woman 19 | wife_preferences = women[preferred_wife] 20 | 21 | if preferred_wife not in wives: # if she is single, let man propose 22 | wives[preferred_wife] = man # if she is single, she will accept 23 | del men[man][0] # delete this woman from man's preferences 24 | single_men.remove(man) # man is no longer single (for now) 25 | else: 26 | fiance = wives[preferred_wife] # get womans current fiance 27 | 28 | rank_of_fiance = wife_preferences.index(fiance) # get fiance rank 29 | rank_of_man = wife_preferences.index(man) # get man rank 30 | 31 | # If man is listed before the fiance on the woman's preferences, 32 | # then the woman accepts the proposal. 33 | if rank_of_man < rank_of_fiance: # if man is ranked higher 34 | wives[preferred_wife] = man # man becomes new fiance 35 | single_men.remove(man) 36 | single_men.append(fiance) # old fiance becomes single 37 | else: 38 | del men[man][0] # delete this woman from man's preferences 39 | 40 | return wives # all engaged pairs (by wife) 41 | 42 | 43 | def main(): 44 | 45 | men = { # man: [list of women where index is rank] 46 | 1: [2, 3, 4, 1], 47 | 2: [2, 1, 3, 4], 48 | 3: [1, 3, 4, 2], 49 | 4: [1, 2, 3, 4] 50 | } 51 | 52 | women = { # woman: [list of men where index is rank] 53 | 1: [2, 4, 1, 3], 54 | 2: [3, 1, 2, 4], 55 | 3: [1, 2, 4, 3], 56 | 4: [1, 2, 4, 3] 57 | } 58 | 59 | pairs = stable_marriage(men, women) 60 | 61 | # Pretty print 62 | print('Engagements\n(M, W)') 63 | for key in pairs: 64 | print('(' + str(pairs[key]) + ',' + str(key) + ')') 65 | 66 | 67 | if __name__ == '__main__': 68 | main() 69 | -------------------------------------------------------------------------------- /Guide/Greedy/Practice/Algorithmic Crush/README.md: -------------------------------------------------------------------------------- 1 | # Algorithmic Crush 2 | This problem comes from [HackerRank](https://www.hackerrank.com/challenges/crush) 3 | 4 | Category: Greedy Algorithms 5 | 6 | Difficulty: Hard 7 | 8 | ## Problem 9 | 10 | ### Overview 11 | - Given a list of size N with elements initialized to zero 12 | - Perform M operations with parameters a (start), b (end), and k (value) 13 | - Each operation adds the value of k to each element from position a (index a-1) to position b inclusively 14 | - Return the value of the element in the list with the greatest final value 15 | 16 | ### Input Format 17 | - The first line contains two integers N and M separated by a space 18 | - The list will be of size N 19 | - The next M lines contain three integers a, b, and k (in this order) each separated by a space 20 | 21 | ### Constraints 22 | - 3 <= N <= 10^7 23 | - 1 <= M <= 2 * 10^5 24 | - 1 <= a <= b <= N 25 | - 0 <= k <= 10^9 26 | 27 | ### Output Format 28 | - A single line with the returned value 29 | 30 | ## Algorithm 31 | 32 | ### Overview 33 | The main realization that helps reduce the complexity of the problem is that one doesn’t need to worry about when a specific operation begins or ends. 34 | 35 | For example, one could have this input: 36 | 37 | 5 2 38 | 39 | 1 3 100 40 | 41 | 3 5 100 42 | 43 | This would result in this final list: 44 | 45 | 100 100 200 100 100 46 | 47 | However, this could also be achieved with the input: 48 | 49 | 5 2 50 | 51 | 1 5 100 52 | 53 | 3 3 100 54 | 55 | In this case, the only facts one needs to take into account are as follows: 56 | - An operation starts adding 100 at element 1 57 | - An operation starts adding 100 at element 3 58 | - An operation stops adding 100 after element 3 59 | - An operation stops adding 100 after element 5 60 | 61 | ### Pseudo Code 62 | Given an array of size 'N' + 1 and two integers 'current' and 'max' initialized to 0 63 | - For each of the elements in the array 64 | - Set the value of the element to 0 65 | - For each of the 'M' operations 66 | - Add the value of 'k' to the element in the (a - 1)th index 67 | - Subtract the value of 'k' from the element in the (b)th index 68 | - For each of the elements in the array 69 | - Add the value of the element to 'current' 70 | - If the value of 'current' is greater than 'max' 71 | - Set 'max' to the value of 'current' 72 | 73 | ### Analysis 74 | All of the loops in the algorithm iterate either over the 'N' + 1 elements in the array or the 'M' operations provided by input. 75 | None of the inner loops contain operations with complexity greater than constant time. 76 | As a result, the algorithm can be classified as O(N). 77 | 78 | ## Conclusion 79 | This problem is particularly good at highlighting the usefulness of eliminating repeated operations. 80 | Without optimization the solution can be costly. 81 | For instance, the naive method to solve this problem involves adding the value of ‘k’ to each element in the list from ‘a’ to ‘b’ inclusively for each of the ‘M’ operations. 82 | The worst case would be if ‘a’ is 1 and ‘b’ is ‘N’ for each of the ‘M’ operations, making this algorithm an O(N*M) operation. 83 | However, there are fairly obvious places where optimization can happen, as shown in this solution. 84 | 85 | Additionally, the solution is quite simple, so one doesn't get sidetracked with the minutiae of implementation. 86 | -------------------------------------------------------------------------------- /Guide/Greedy/Practice/Algorithmic Crush/solution.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() 4 | { 5 | long long caseSize; // Size of list to be operated on (N) 6 | int operations; // Number of operations (M) 7 | long long start; // Temporary storage for start integer (a) 8 | long long stop; // Temporary storage for stop integer (b) 9 | long long operand; // Temporary storage for operand (k) 10 | long long * operands; // Pointer to array of operands 11 | long long current = 0; // Current value at given index of array 12 | long long max = 0; // Maximum value for array at given index 13 | 14 | std::cin >> caseSize >> operations; 15 | 16 | /* 17 | One is added to 'caseSize' because 'operands' will hold both 'start' 18 | and 'end' values. The 'end' values will be shifted one position to 19 | the right of their actual end point. This is because 'end' values are 20 | treated like negatives, but the operation in this problem needs to be 21 | inclusive. 22 | */ 23 | caseSize++; 24 | operands = new long long[caseSize]; 25 | 26 | // Initializing the elements of 'operands' to zero 27 | for(long long i = 0; i < caseSize; i++) 28 | { 29 | operands[i] = 0; 30 | } 31 | 32 | // Reading input into 'operands' 33 | for(int i = 0; i < operations; i++) 34 | { 35 | std::cin >> start >> stop >> operand; 36 | 37 | /* 38 | Decrementing 'start' accounts for the fact that arrays start at 39 | zero. The reason that only start is decremented is that stop will 40 | be shifted to the right anyway. 41 | */ 42 | start--; 43 | 44 | operands[start] += operand; 45 | operands[stop] -= operand; 46 | } 47 | 48 | // Calculating the maximum value 49 | for(long long i = 0; i < caseSize; i++) 50 | { 51 | /* 52 | 'current' acts as an accumulator. 'start' values are positive, 53 | causing 'current' to increase. 'end' values are negative, causing 54 | 'current' to decrease. 55 | */ 56 | current += operands[i]; 57 | 58 | if(max < current) 59 | max = current; 60 | } 61 | 62 | std::cout << max; 63 | 64 | delete [] operands; 65 | 66 | return 0; 67 | } 68 | -------------------------------------------------------------------------------- /Guide/Greedy/Practice/Kindergarten Adventure/README.md: -------------------------------------------------------------------------------- 1 | # Kindergarten Adventure 2 | [https://www.hackerrank.com/challenges/kindergarten-adventures](https://www.hackerrank.com/challenges/kindergarten-adventures) 3 | 4 | Category: Greedy 5 | 6 | Difficulty: Medium 7 | 8 | 9 | ## Problem 10 | 11 | ### Overview 12 | ####Given: 13 | * A set of student IDs {1, 2, ..., n}. 14 | * The number of time each student, i, needs to finish the project, ti. 15 | 16 | ####Goal: 17 | * Pick a student ID that the teacher can start with that: 18 | * will allow the most students to finish their project. 19 | * that has the lowest ID number. 20 | 21 | ## Algorithm 22 | ### Overview 23 |

24 | Without much thought, it is fairly easy to come up with the brute force way to solve this problem. 25 | If you started at each child you could count the number of children done and pick the starting child that allowed the most children to finish. 26 | However, this algorithm will have an O(n2) time complexity and is too slow. 27 | However, we can convert it into Algorithmic Crush, a problem we efficiently solved in another section. 28 |

29 | 30 | #### Important things to note: 31 | * Each child, i, has enough time to finish if the teacher starts in the range [ (i + 1) % n, ((i + 1) + (n - ti)) % n). 32 | * if (i + 1) % n == ((i + 1) + (n - ti)) % n then there is nothing in the range. 33 | * The algorithm used to solve Algorithmic Crush will not work if there are ranges that loop back to zero. 34 | * The ranges could be [ i + 1, (i + 1) + (n - ti) ) as long as the array you use to store the values is 2n + 1 in size. 35 | * For this the number of children that will be done when you start at child i, after preforming the Algorithmic Crush algorithms, is the number of children that finish if position i is the start plus the number that finish if position i + n is the start. 36 | 37 | ### Pseudo Code 38 | 1. Read in the number of children into n. 39 | 2. Make an array called times of size n. 40 | 3. Read in the additional time each child, i, needs into times[i]. 41 | 4. Make an array called ranges of size 2n + 1. 42 | 5. For i = 0 to n: 43 | 1. ranges[i + 1] = ranges[i + 1] + 1 44 | 2. ranges[(i + 1) + (n - times[i])] = ranges[(i + 1) + (n - times[i])] - 1 45 | 6. For i = 1 to 2n + 1: 46 | 1. ranges[i] = ranges[i] + ranges[i - 1] 47 | 7. Make an array called numComplete of size n. 48 | 8. For i = 0 to n: 49 | 1. numComplete[i] = ranges[i] + ranges[i + n] 50 | 9. Find the biggest number in numComplete and print its index plus one. 51 | 52 | ### Analysis 53 |

54 | The biggest loop in this algorithm goes from 1 to 2n + 1. Becasue time complexity drops constants, 55 | the time complexity is O(n), even though the full formula is n + 2n + n + n => 5n. While this is O(n), there is a more efficient O(n) solution. 56 | It follows this same basic algorithm, but it gets rid of most of the loops. Its full formula is 2n. It is also included in the solution file. 57 |

58 | 59 | ## Conclusion 60 |

61 | Many computer science problems can be reduced to other problems that have already been solved 62 | or that are simpler. This problem is an example of reducing a problem to another problem 63 | that has already been solved. Becasue this problem was reducable, it ran faster and most of the 64 | algorithm to solve it was already thought up. This saved time when writing the code. 65 |

-------------------------------------------------------------------------------- /Guide/Greedy/Practice/Kindergarten Adventure/optimisedSolution.java: -------------------------------------------------------------------------------- 1 | import java.util.Scanner; 2 | 3 | public class KindergartenAdventureOptimised 4 | { 5 | public static void main(String[] args) 6 | { 7 | Scanner reader = new Scanner(System.in); 8 | 9 | int n = reader.nextInt(); //the number of students 10 | 11 | int[] times = new int[n]; //times[i] is the time needed by student i-1 12 | int[] ranges = new int[2*n + 1]; //range[i] is the number of students that 13 | //would be able to finish if the teacher 14 | //started with student i+1 15 | 16 | /* 17 | Read in the times for each student into times. 18 | For each input add one to the starting index and subtract one from the index after the ending index. 19 | Compute all the numbers in ranges from 1 to n-1. 20 | */ 21 | for (int i = 0; i < n; i++) 22 | { 23 | times[i] = reader.nextInt(); 24 | ranges[i + 1] += 1; 25 | ranges[(i + 1) + (n - times[i])] -= 1; 26 | if(i > 0) 27 | { 28 | ranges[i] += ranges[i - 1]; 29 | } 30 | } 31 | 32 | /* 33 | Compute all the numbers in ranges from n to 2n. 34 | Find the position for the teacher to start at that would allow the most students to finish. 35 | */ 36 | int maxPos = 0; //The first student where the maximum number of students will finish 37 | for (int i = n; i < 2*n + 1; i++) 38 | { 39 | ranges[i] += ranges[i - 1]; 40 | ranges[i - n] += ranges[i]; 41 | if(ranges[i - n] > ranges[maxPos]) 42 | { 43 | maxPos = i - n; 44 | } 45 | } 46 | 47 | //Print the answer. 48 | System.out.println(maxPos + 1); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Guide/Greedy/Practice/Kindergarten Adventure/solution.java: -------------------------------------------------------------------------------- 1 | import java.util.Scanner; 2 | 3 | public class KindergartenAdventure 4 | { 5 | public static void main(String[] args) 6 | { 7 | Scanner reader = new Scanner(System.in); 8 | 9 | int n = reader.nextInt(); //the number of students 10 | int[] times = new int[n]; //times[i] is the time needed by student i-1 11 | 12 | //Read in the time for each student into times. 13 | for (int i = 0; i < n; i++) 14 | { 15 | times[i] = reader.nextInt(); 16 | } 17 | 18 | int[] ranges = new int[2*n + 1]; //range[i] + range[(i + n) % (2n)] is the number of students 19 | //that would be able to finish if the teacher started with student i+1 20 | 21 | //for each input add one to the starting index and subtract one from the index after the ending index. 22 | for (int i = 0; i < n; i++) 23 | { 24 | ranges[i + 1] += 1; 25 | ranges[(i + 1) + (n - times[i])] -= 1; 26 | } 27 | 28 | //Compute all the numbers in ranges. 29 | for (int i = 1; i < 2*n + 1; i++) 30 | { 31 | ranges[i] += ranges[i - 1]; 32 | } 33 | 34 | int[] numComplete = new int[n]; //numComplete[i] is the number of students that would be able to 35 | //finish if the teacher started with student i+1 36 | 37 | //Compute all the numbers in numComplete using ranges. 38 | for (int i = 0; i < n; i++) 39 | { 40 | numComplete[i] = ranges[i] + ranges[i + n]; 41 | } 42 | 43 | //Find the maximum number of students that can finish starting with any student and the number of that student. 44 | int max = numComplete[0]; //The maximum number of students that could finish starting with any student. 45 | int maxPos = 0; //The first possition in numComplete where the value max occurs. 46 | for (int i = 1; i < n; i++) 47 | { 48 | if (numComplete[i] > max) 49 | { 50 | max = numComplete[i]; 51 | maxPos = i; 52 | } 53 | } 54 | 55 | //Print the answer. 56 | System.out.println(maxPos + 1); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Guide/Greedy/README.md: -------------------------------------------------------------------------------- 1 | # Greedy Algorithms Guide 2 | 3 | ## [Overview](https://www.tutorialspoint.com/data_structures_algorithms/greedy_algorithms.htm) 4 | By maximizing or minimzing a metric, it's possible to solve many problems. Finding the right metric and writing an algorithm to optimize based off of it is the essence of good greedy algorithms. 5 | 6 | ## Additional Resources 7 | More code or math focused examples can both be helpful, as it sometimes requires multiple perspectives to understand a new topic. Both are listed below. 8 | 9 | ### Extended Examples 10 | A [more mathematical explanation](https://people.eecs.berkeley.edu/~vazirani/algorithms/chap5.pdf) can be found from Berkeley. It also contains several extended examples of how to apply greedy algorithms. 11 | 12 | ### Extended Explanation 13 | More code focused explanations can be found [here](http://www.geeksforgeeks.org/greedy-algorithms-set-1-activity-selection-problem/). 14 | -------------------------------------------------------------------------------- /Guide/README.md: -------------------------------------------------------------------------------- 1 | # Guide 2 | 3 | This folder serves as the top level for all the problems, solutions and all other material that will be used by a person wishing to learn about coding problems. 4 | Our current folder organization is inspired by [HackerRank](https://www.hackerrank.com/domains/algorithms). 5 | Here you can browse through the current problems we have solved and written solutions for, or you can click a link below to quickly get to the problem you want. 6 | 7 | # Problems 8 | 9 | | Problem | Category | Difficulty | Language | 10 | |-------------------------------------------------------------------------------|---------------------|------------|----------| 11 | | [Bonetrousle](/Guide/Constructive/Bonetrousle) | Constructive | Medium | Java | 12 | | [New Year Chaos](/Guide/Constructive/New%20Year%20Chaos) | Constructive | Medium | CPP | 13 | | [Contacts](/Guide/Data%20Structures/Contacts) | Data Structures | Medium | Java | 14 | | [Abbreviation](/Guide/Dynamic%20Programming/Practice/Abbreviation) | Dynamic Programming | Medium | Java | 15 | | [Maximum Path Sum](/Guide/Dynamic%20Programming/Practice/Maximum%20Path%20Sum) | Dynamic Programming | Easy | Java | 16 | | [Nikita And The Game](/Guide/Dynamic%20Programming/Practice/Nikita%20And%20The%20Game) | Dynamic Programming | Medium | C++ | 17 | | [Segmented Least Squares](/Guide/Dynamic%20Programming/Educational/Segmented%20Least%20Squares) | Dynamic Programming | Hard | Python | 18 | | [Jack goes to Rapture](/Guide/GraphTheory/JackGoesToRapture) | Graph Theory | Medium | Java | 19 | | [Journey to the Moon](/Guide/GraphTheory/Journey%20to%20the%20Moon) | Graph Theory | Medium | CPP | 20 | | [Algorithmic Crush](/Guide/Greedy/Practice/Algorithmic%20Crush) | Greedy | Hard | CPP | 21 | | [Kindergarten Adventure](/Guide/Greedy/Practice/Kindergarten%20Adventure) | Greedy | Medium | Java | 22 | | [Stable Marriage](/Guide/Greedy/Educational/Stable%20Marriage) | Greedy | N/A | Python | 23 | | [Interval Scheduling](/Guide/Greedy/Educational/Interval%Scheduling) | Greedy | Medium | Python | 24 | | [Interval Partitioning](/Guide/Greedy/Educational/Interval%20Partitioning) | Greedy | Medium | Python | 25 | | [Min Max Lateness](/Guide/Greedy/Educational/Minimizing%20Maximum%20Lateness%20Scheduling) | Greedy | Medium | Python | 26 | | [Shortest Paths in a Graph](/Guide/Greedy/Educational/Shortest%20Path) | Greedy | Hard | Python | 27 | | [Minimum Spanning Tree](/Guide/Greedy/Educational/Minimum%20Spanning%20Tree) | Greedy | Medium | Python | 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TTU Applied Algorithms 2 | Name under construction. 3 | 4 | # Overview 5 | This repo serves as a central location for all things related to solving competitive programming problems. Here is where you can find guides and solutions to various types of programming problems in addition to tutorials on different types of problems. 6 | 7 | # Purpose 8 | The purpose of this community is to bring experienced and beginner programmers together in a relaxed environment. Here the experienced programmers can store not just their solutions to various problems found online, but a detailed solution guide that is intended to walk beginners through their thought process and to show them how they solved the problem. We hope that by doing this, beginner programmers can get past their fear of these problems and start to enjoy them. 9 | 10 | We, the creators of this community, understand that at first, these problems can seem very intimidating and at first, one can feel like they lack the skills needed to solve these problems. Our hope, with this project, is to eliminate those fears and ideas and instead, show how it's not too difficult to get involved in and it doesn't require too much upfront knowledge to get started. 11 | 12 | Again, this repo is designed for beginners so we will have guides and resources at every step of the way. No matter you skill level, you _can_ get into online programming problems. 13 | 14 | # F.A.Q. 15 | We understand that you might have a lot of questions concerning this and we hope to answer them all. If you have a question that is not answered below, feel free to contact us and we will try our best to answer you! 16 | 17 | ## Why do I want to do these online problems? 18 | Are you a Computer Science or related major? Do you want a good software engineering job? Do you want to keep your programming skills up to date? These types of problems can help with all of that and more! 19 | Here is what you can get out working on these types of problems: 20 | - **Technical interview prep**: These types of problems are _very_ similar to how technical interview questions are structured. So if you plan on interviewing for a technical internship or job, these questions are the best way to prepare yourself. 21 | 22 | - **Keeping your programming skills sharp and refined**: Since these problems are short and require a solid knowledge of algorithms and data structures, by working on these problems regularly, you can keep not just your programming knowledge of languages you know, but use these problems to gain skills in languages you aren't familiar with. 23 | 24 | - **It's fun**: This one might be harder to believe, but after you keep really into these types of problems they can seem fun. If you end up really enjoying these problems, you might want to get involved in online programming competitions! And hey, that's something else you can put on your resume. 25 | 26 | ## What does it take to join your community? 27 | Are you a student at Texas Tech University? Yes? Cool! That's all it takes! 28 | 29 | ## How can I get involved in your community? 30 | We live on Slack. Check us out at [ttucs.slack.com](https://ttucs.slack.com). Must have a `@ttu.edu` email to register. 31 | 32 | ## How can I contribute to this repo? 33 | To contribute, please checkout our [Contributing guidelines](/.github/CONTRIBUTING.md) 34 | 35 | ## What sites do you get your problems from? 36 | Currently, all the problems we've worked on have come from [HackerRank](https://www.hackerrank.com/domains). But we do intend to draw from other sites. 37 | 38 | 39 | # Acknowledgements 40 | This community would like to give a big thanks to [Dr. Susan Mengel, Ph.D.](http://www.depts.ttu.edu/coe/dean/faculty/faculty.php?name=Susan%20A.%20Mengel) for all of her guidance and input into this project. 41 | -------------------------------------------------------------------------------- /Templates/README.md: -------------------------------------------------------------------------------- 1 | # Templates 2 | 3 | This folder serves to hold all the template files to be used by contributors to this project. 4 | -------------------------------------------------------------------------------- /Templates/problem-template.md: -------------------------------------------------------------------------------- 1 | # [Problem Name] 2 | [Link] 3 | 4 | [Tags/Keywords/Categories] 5 | 6 | [Rating/Difficulty] 7 | 8 | 9 | ## Problem 10 | 11 | ### Overview 12 | [A tl;dr of the statement at the very least. Break out the meat from the problem] 13 | 14 | ## Algorithm 15 | ### Overview 16 | [A place for the creators thoughts on the algorithm before getting into it] 17 | ### Pseudo Code 18 | [The algorithm] 19 | ### Analysis 20 | [Analyze the algorithm, here's where things such as complexity can be discussed] 21 | 22 | ## Conclusion 23 | [Any final thoughts here, maybe discuss other ways to solve the problem that would be equally efficient] 24 | --------------------------------------------------------------------------------