Exercise 7: Recursions
Prerequisite
You have completed Exercise 6.
Learning Outcomes
Able to model a problem as a search problem and solve it recursively.
Deadline
This exercise is part of the CS1010 formative assessment. Submit your solution before 4 November 2024, 23:59 to receive feedback and earn your achievement badges.
Acceptance Link
The link to accept the exercise is not available publicly. Visit Canvas for the link.
Concepts and Difficulty
Question | Recursion | Branch-and-Bound | Difficulty | |
---|---|---|---|---|
1 | Fill | |||
2 | Maze | |||
3 | Walk | |||
4 | Sudoku |
Question 1: Fill
One of the basic operations in a drawing application is the fill operation (aka bucket fill). You will implement this operation in this assignment.
An image can be considered as a 2D array of pixels. Each pixel has a color, represented by three integers, corresponding to the red, green, and blue components of the color. We say that two pixels are adjacent to each other if they share a common edge. So a pixel can have at most four adjacent pixels.
We denote the top left pixel to be (0,0). The indices increase towards the right and down. For instance, if we have an image of width 20 by 30, then the top-right pixel has the coordinate (0, 19); the bottom-left pixel has the coordinate (29, 0); and the bottom-right has the coordinate (29, 19).
An image can be divided into one or more segments. A segment is a set of pixels (i) with the same color, and (ii) given any two pixels \(p\) and \(q\) in a segment, we can find a series of adjacent pixels in the segment going from \(p\) to \(q\).
The fill operation takes in as an input the (\(x\),\(y\)) coordinate of a pixel \(p\), and a color \(c\) to fill. The operation then replaces the color of all pixels belonging to the same segment as \(p\) with the given color \(c\).
Write a program, fill.c
, that reads from standard input two positive integers \(m\) and \(n\) in the first line. \(m\) refers to the number of rows, and \(n\) is the number of columns.
Then read \(3mn\) integers representing an \(m\) by \(n\) image from the standard inputs. Each tuple of 3 integers refers to the color of a pixel. The pixels are read from left to right, top to bottom. Thus, the first three integers refer to the color of the pixel at (0, 0); the next three integers refer to the color of the pixel at (0, 1), etc.
The next input is a positive integer \(q\), which is the number of fill operations. Following this, there are \(q\) lines. Each line has two integers \(x\), \(y\), and three integers, \(r\), \(g\), and \(b\), corresponding to a color. Each line specifies a fill operation, to fill the segmented pixel \((x, y)\) is in with the color \((r, g, b)\).
The program shall print to standard output, the image after executing all the given fill operations. The image should be printed in the following format:
- The first line starts with a string "P3", followed by a space, followed by two integers, \(n\), and \(m\), that correspond to the width and height of the image. This is followed by a space, and then the number 255.
- The remaining lines contain the color of each pixel, one color (i.e., three integers) per line. The pixels should be printed from left to right, top to bottom.
An example output:
1 2 3 4 5 |
|
You should use recursion to implement the fill operation.
Your solution must take no more than \(O(mnq)\) time.
You can visualize the resulting image using the viu
command:
1 |
|
Note that viu
might rescale your image if your terminal is too small.
Tips
Similar to social
, your code would be simpler if you separate the representation of the image from the underlying 2D array. Writing functions to retrieve, update, and compare the color of a pixel of an image would make your code much simpler and easier to read, write, and debug.
Your solution must take no more than \(O(mnq)\) time to be categorized as "Excellent".
Question 2: Maze
Luke woke up and found himself trapped in a maze. He has to find his way out if there is one!
The maze can be simplified as a grid of (\(m \times n\)) cells. Each cell can be of the following:
-
'#' denotes a segment of a wall. No one can pass through the walls.
-
'.' denotes an empty space.
-
'@' denotes where Luke is currently. It is also an empty space.
Anytime Luke reaches a border cell (a cell in either the top-most or bottom-most row or left-most or right-most column), he escapes the maze and can go save his friends Han and Leia. He can only move from one empty space to another adjacent cell in one step. Two cells are adjacent if they share a common edge.
Luke took CS1010, and he got a concrete plan to seek a way out by trial and error. He follows strictly the following strategy to find a way through the maze starting from his initial position. At each time step,
-
Luke looks for an empty adjacent cell that has never been visited yet, in the sequence of up/right/down/left to the current cell he is at. If there is an empty adjacent cell, he moves to that cell. The cell he moves to is now visited.
-
If no empty, unvisited, adjacent cell exists, he backtracks on the path that he comes from, moving one step back, and repeats 1 again.
In this way, Luke systematically explores the maze, with no repetitive visit of any cell more than once except when he backtracks. he will stop when successfully escapes the maze, or finds that there is no way out after backtracking all the way back to the original position. He is completely trapped within the maze and now must wait for his friends to come and free him.
Write a program maze.c
, that reads from standard input. First, read two positive integers \(m\) and \(n\), followed by \(m\) lines of \(n\) characters in each line that represents the maze setup. One and only one @
will be present in the maze setup.
The program then prints, to standard output, an animation of \(k\) iterations. The output should only contain \(m\) rows with \(n\) characters in each row, with an additional row at last. Similarly, you must use #
to represent a wall, a .
to represent empty space, and @
to represent where Luke is at. After printing the maze, your program prints the number of steps that Luke has made.
You should use recursion to explore the maze and look for a way out.
Here is an example. The following is the starting position of Luke and the maze.
1 2 3 4 5 6 7 8 9 10 |
|
Luke first moves five steps up:
1 2 3 4 5 6 7 8 9 10 |
|
At this point, Luke is stuck since there is no more adjacent empty cell that has not been visited. Luke then backtracks:
1 2 3 4 5 6 7 8 9 10 |
|
Luke then moves down:
1 2 3 4 5 6 7 8 9 10 |
|
Then right:
1 2 3 4 5 6 7 8 9 10 |
|
Then up:
1 2 3 4 5 6 7 8 9 10 |
|
Then right (two steps) and then down (two steps) and then right (two steps):
1 2 3 4 5 6 7 8 9 10 |
|
Then Luke moves up and left, and he is stuck again.
1 2 3 4 5 6 7 8 9 10 |
|
At this point, he backtracks:
1 2 3 4 5 6 7 8 9 10 |
|
Moves right, and up, and stuck again!
1 2 3 4 5 6 7 8 9 10 |
|
1 2 3 4 5 6 7 8 9 10 |
|
This time he found his way out!
1 2 3 4 5 6 7 8 9 10 |
|
It took him a total of 50 steps to exit the maze.
Your solution must take no more than \(O(mn)\) time to be graded as "Excellent".
Hint: You need to strictly follow the described strategy and sequence of exploration. You need to print the initial maze and the final maze to match the expected output.
We have provided a function print_maze
to draw the maze in the skeleton file.
Sample Run
You can use the sample program ~cs1010/ex07/maze
on the given inputs to view the animation.
Note that how the maze is printed on the standard output is different from when it is written to a file.
Question 3: Walk
Many cities in the world, such as New York City, are laid out as a grid, with streets running in north-south directions and east-west directions. Buildings between two streets are called blocks.
Suppose we start at a junction of two streets, and we wish to walk to another junction that is \(y\) blocks to the north and \(x\) blocks to the east, how many possible paths are there to walk to the destination?
For example, suppose we want to walk to a junction that is one block to the north and one block to the east, and we can only walk eastward and northward, there are two possible paths. We can walk east for one block, then walk north for another block. Or, we can walk north first for one block, then walk east for another block.
The figure below illustrates the scenario, where we are positioned at source D and we wish to go to destination B. The two possible paths are DAB and DEB.
1 2 3 |
|
Suppose now we want to walk to C, a junction that is two blocks to the east, and one block to the north. There are three different possible paths. The three paths are DABC, DEBC, and DEFC.
Write a program walk
that reads in two integers \(x\) and \(y\) \((x \ge 0, y >= 0)\), and prints the number of possible paths we can walk to the destination that is \(x\) block to the east and \(y\) block to the north.
Your solution must take no more than \(O(xy)\) time.
Hint: Think recursively, but solve iteratively.
Sample Runs
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Question 4: Sudoku
This question is inspired by The Prime Minister of Singapore, Lee Hsien Loong, who once wrote a sudoku solver in C++:
In the game of sudoku, we are given a \(9 \times 9\) grid, with some of the cells filled with numbers 1 to 9. We must fill up the rest of the cells with 1 to 9, such that no number repeats itself in each row, in each column, and in each of the nine $3 \times 3$ subgrids.
Write a program, sudoku.c
, that reads in a sudoku puzzle, and search for a solution to the puzzle, using the following algorithm:
-
Start from the top left, and scan left to right, top to bottom.
-
If an empty cell is encountered, try to fill it up with a digit, starting with 1, 2, .. until 9. For each digit we try, check if it is possible to find a solution, and if so, solve the updated puzzle recursively.
-
If a solution is found, print the solution and stop searching. Otherwise, (backtrack and) try a different digit, until either we have tried all digits or a solution is found.
The input to the program consists of 9 lines, 9 characters in each line, representing the puzzle. The input contains only the digits 1 to 9 and the .
character, which represents the empty cell.
The output contains the steps of the search, printed with the print_sudoku
function provided. At the end of the program, either the solution is printed or the string "no solution" if the sudoku cannot be solved.
Your code should take less than 5s to run for each test case given.
Sample Run
You can try to run the program ~cs1010/ex07/sudoku
to see the animation of how the solution is expected to work.
Note that how the puzzle is printed on the standard output is different from when it is written to a file.