COMP1110/6710 Final Exam(2020 S1)
Question 1
Q1.1 A
Print the Collatz sequence for the value n.If the input value is less than one, do nothing.Otherwise, print the input value n on a line by itself, and then:
- if the input value is even, call tao again, passing in (n/2);
- if the input value is odd and greater than one, call tao again, passing in (n*3+1);
- if the input value is one, do nothing.
Special rule (not part of the Collatz sequence):If the input value is 13120, print the string “Tao!” on a line by itself and then exit (without printing anything further).
Solution
This is a very simple problem.
1 | static void tao(int n) { |
Q1.1 B
This function takes a positive integer, n, and returns an array of ints containing all prime factors of that integer in ascending order.
For example:
- factors(6) returns {2, 3}
- factors{7} returns {7}
- factors{24} returns {2, 2, 2, 3}
If n is less than 2, an empty array is returned.
Solution
To factorize n
into prime factors, we should find the smallest prime number k
firstly, and then follow these steps:
If this prime number equals
n
, it means the process of factorization is complete, and we should print it.If
n > k
andn
is divisible byk
, we should print the value ofk
and use the quotient ofn
divided byk
as the new positive integern
, then repeat step one.If
n
is not divisible byk
, usek + 1
as the newk
value, and repeat step one.
1 | public static int[] factors(int n) { |
Q1.2 A
This function takes an array of integers and returns an integer that is the average of the ‘included’ integers in the array, or -1 if there are no ‘included’ integers in the array.
An array element is ‘included’ if:
it is greater than or equal to zero, and
it appears earlier in the array than the MAGIC number (999)
Because the return value is an integer, the average should be calculated using integer division.
Some examples:
[ 1, 3, 999] -> 2
[ 1, 6, 999, 4] -> 3
[ 1, -3, 5, 999] -> 3
Solution
We just need to calculate the sum of all the included integers and then divide it by the number of included integers.
1 | public static int avg(int[] in) { |
Q1.2 B
This function accepts an Individual ancestor representing the root (common ancestor) of a family tree and the name of a target individual to find within that family tree, and returns a string representation of all the ancestors of that individual, each separated by the string " born of ".
If target name matches the name of ancestor, then only the target name is returned.
If the target name is not found within the family tree descended from ancestor, then null is returned.
For example, given an Individual homer representing a person named “Homer”, who has children named “Lisa” and “Bart”:
getAncestry(homer, “Lisa”) returns “Lisa born of Homer”;
getAncestry(homer, “Bart”) returns “Bart born of Homer”; and
getAncestry(homer, “Homer”) returns “Homer”; and
getAncestry(homer, “Barney”) returns null
Note: each individual has only one parent in the family tree.
Solution
Due to the necessity of inserting ‘born of’, we need to retrieve a ancestor string to fulfill the output. The retrieval of the ancestor string requires a depth-first search on a family tree. Here is a family tree of the Simpson family:
1 | Stephanie__Ridge__Thomas__Douglas |
The search for R.J. unfolds as follows:
Stephanie’s children are Ridge, Kristen, Angela, Thorne, Alexandria, and Felicia. Therefore, we start by searching Ridge’s children. Ridge’s children are Thomas, Steffy, Phoebe, and R.J. So, we search Thomas’s children. Thomas’s children are Douglas. Douglas doesn’t have any children, so the search ends. We return to Thomas’s level. Since Thomas’s children have been searched, we return to Ridge’s level. Next, we search Ridge’s next child, Steffy. Steffy’s children include Kelly. We search Kelly’s level, but Kelly doesn’t have any children, so the search ends. We return to Steffy’s level. Since Steffy’s children have been searched, we return to Ridge’s level. Next, we search Ridge’s child Phoebe. Phoebe doesn’t have any children, so the search ends. We return to Ridge’s level and search for R.J., finding the target. We then return step by step based on the levels to obtain the result.
The code design based on this process is as follows:
1 | static boolean recGetAncestry(ArrayList<String> ansList,Individual ancestor1, String ancestor) { |
Q1.3 A
Get all valid crop rotations that can be composed from the provided set of vegetable crops for the given number of seasons.
A crop rotation is a sequence of vegetable crops to plant.One crop is planted per season, and any crop may be planted at most once.Crops must be planted in order of their Group according to the following
rules:
- a LEGUME may only be followed by a BRASSICA;
- a BRASSICA may only be followed by an ALLIUM;
- an ALLIUM may only be followed by a FRUITING crop; and
- a FRUITING crop may only be followed by a LEGUME.
For example, the call getAllRotations([Tomato (fruiting), Onion (allium)], 2) returns a set containing a single rotation: - [Onion (allium), Tomato (fruiting)]
because an ALLIUM may only be followed by a fruiting crop.
The call getAllRotations([Tomato (fruiting), Kale (brassica), Onion (allium), Pea (legume)], 4) returns a set containing four rotations: - [Kale (brassica), Onion (allium), Tomato (fruiting), Pea (legume)]
- [Onion (allium), Tomato (fruiting), Pea (legume), Kale (brassica)]
- [Pea (legume), Kale (brassica), Onion (allium), Tomato (fruiting)]
- [Tomato (fruiting), Pea (legume), Kale (brassica), Onion (allium)]
If no valid crop rotation can be found, an empty list is returned.
For example, the call getAllRotations([Tomato (fruiting), Gai Lan (brassica)], 2) returns an empty set, because a FRUITING crop cannot be followed by a BRASSICA, and a BRASSICA cannot be followed by a FRUITING crop.
Solution
The problem is a typical backtracking problem. We can use a recursive method to generate all the possible rotations and then check if they are valid. If they are valid, we add them to the result set.
1 | private static boolean isValidRotation(Vegetable v1, Vegetable v2) { |
The isValidRotation method is used to validate whether two vegetable crops can form a valid crop rotation. It determines if the second crop v2 complies with rotation rules by checking the group of the first crop v1.
The generateRotations method is employed to produce all possible crop rotation schemes. It traverses all potential combinations of crops using recursion and ensures the generation of valid rotations through backtracking. Here’s the gist of this method:
Firstly, we check if the current rotation’s length equals the given number of seasons. If it does, it indicates that the current rotation scheme is complete. We add it to the result set and terminate the current recursion branch.
If the rotation’s length hasn’t reached the specified number of seasons, we continue attempting to add different crops to the current rotation scheme.
We iterate through the available crops. For each crop, we verify if it’s already in the current rotation. If not, we further check if it satisfies the rotation rule, i.e., if the current crop can follow the previous one.
If the current crop adheres to the rotation rule, we add it to the rotation scheme and recursively call the generateRotations method to continue generating rotation schemes down the line.
Upon recursive return, we perform backtracking by removing the last added crop to explore other possibilities.
The getAllRotations method serves as the interface method, used to invoke generateRotations for generating all possible rotations and returning a collection containing all rotation schemes.
Q1.3 B
This question is very similar to Q1.3 A, and the solution is almost identical. Therefore, I won’t explain further.
Q1.3 C
A Sudoku puzzle consists of the numbers 1-9 arranged in a 9x9 grid, divided into nine 3x3 subgrids.In a valid solution for the puzzle, each of the numbers 1-9 appears only once in each row, column and subgrid. For example, the following is a valid solution:
1 | ------------------------- |
whereas the following partial layout is not valid, because the number 5 appears twice in the upper-left subgrid.
1 | --------------- |
The puzzle is specified as a 9x9 array of integers which holds the rows and columns of numbers in each cell of the puzzle.
For example, the number in the second row and third column of the puzzle is held in array element a[1][2].
If the setup gives zero for a number, it means that value for that cell is not specified, and must be solved. In other words, zero must be replaced with a number 1-9 to create a valid solution.
For example, the following is one possible setup for the valid solution given above:
1 | ------------------------- |
Solution
The process of solving Sudoku can be implemented recursively. Firstly, we check if the Sudoku puzzle is fully filled. If it is, it means the Sudoku puzzle is solved. Then, we find the next empty cell and attempt to fill it with a number. For each empty cell, we try filling it with numbers from 1 to 9, and then check if it satisfies the Sudoku rules. If the number we’re trying to fill already exists in the same row, column, or subgrid, it’s not a valid move, and we backtrack to the previous state. The Sudoku solving process continues until all empty cells are filled, at which point the Sudoku puzzle is considered solved.
1 | public static int[][] solve(int[][] setup) { |
The remaining questions are relatively simple, please refer to the code yourself.