Divide and Conquer Algorithm - Java Code Implementation with Checkerboard Coverage Problem

Basic Concepts of Divide and Conquer Algorithms

   In computer science, divide and conquer is an important algorithm. The literal interpretation is "divide and conquer", which is to divide a complex problem into two or more identical or similar sub-problems, and then divide the sub-problems into smaller sub-problems... until the final sub-problem can be simply solved directly , the solution of the original problem is the combination of the solutions of the sub-problems . This trick is the basis of many efficient algorithms, such as sorting algorithms (quick sort, merge sort), Fourier transform (fast Fourier transform)...

    The computational time required for any computer-solvable problem is related to its size. The smaller the problem size, the easier it is to solve directly, and the less computational time is required to solve the problem. For example, for a sorting problem of n elements, when n=1, no computation is required. When n=2, only one comparison can be made to sort. When n=3, only 3 comparisons can be made, . . . And when n is large, the problem is not so easy to deal with. It is sometimes quite difficult to directly solve a large-scale problem.


Application of divide and conquer algorithm:

    1) The smaller the size of the problem, the easier it is to solve

    2) The problem can be decomposed into several smaller problems of the same size

    3) The solutions of the sub-problems can be combined into the solution of the problem;

    4) Each sub-problem is independent of each other


Here is an example of the checkerboard coverage problem:

        Problem description: In a chessboard composed of 2^k×2^k (k≥0) squares, there is exactly one square that is different from other squares, and this square is called a special square. Obviously, there are 4^k possible positions for a particular square on the board, and thus 4^k different boards. The chess cover problem requires that all squares on a given chessboard (4x4 chessboard) be covered with 4 different shapes of L-shaped dominoes as shown, except the special ones, and any 2 L-shaped dominoes Dominos must not overlap.


Solutions:

        When k>0, the 2^k×2^k chessboard can be divided into 4 sub-chessboards of 2^(k-1)×2^(k-1). After this division, since the original chessboard has only one special square, only one of the four sub-chessboards contains this special square, and the remaining three sub-chessboards have no special square. In order to convert these 3 sub-boards without special squares into special chessboards for recursive solution, an L-domino can be used to cover the meeting of the 3 smaller chessboards, thus transforming the original problem into 4 smaller chessboards Scaled checkerboard coverage problem. This partitioning strategy is used recursively until the board is divided into 1×1 subboards.

Specific steps:

    1. We first divide the entire chessboard into four pieces: upper left, upper right, lower left, and lower right , and then determine which piece the special square is in.

    2. If it is in the upper left area, we will divide this area into four parts until we find it.

    3. Other areas without special squares, we need to start filling L-shaped cards.

    4. We take, first fill in the most surrounding L-shaped cards. As shown below:

    5. We found that after dividing into 4 blocks, the 3 areas without special squares only need to be filled as shown above, and there is just an L-shaped area left in the middle, so we set the square near the middle in each area as the current sub The special block in the matrix, the purpose of this is to make each division exactly 3 more blocks, so that the L-shaped area can be placed.


    6. For the area where there are special squares before, we can directly recursively divide the area (as shown in the upper left) area, and then the next filling will be completed.

For the specific implementation, please see the code (java implementation):

public class ChessProblem {
	
	int size;//capacity
	int[][] board;//chessboard
	int specialROW;//Special point abscissa
	int specialCOL;//Special point ordinate
	int number = 0;//L-shaped number
	
	public ChessProblem(int specialRow, int specialCol, int size) {
		this.size = size;
		this.specialCOL = specialCOL;
		this.specialROW = specialROW;
		board = new int[size][size];
	}
	
	//specialROW   特殊点的行下标
	//specialCOL   特殊点的列下标
	//leftRow      矩阵的左边起点行下标
	//leftCol      矩阵左边起点的列下标
	//size         矩阵的宽或者高
	
 	public void setBoard(int specialROW, int specialCOL, int leftROW, int leftCOL, int size) {
		if (1 == size) {
			return;
		}
		
		int subSize = size / 2;
		number++;
		int n = number;//注意这里一定要吧number存在当前的递归层次里,否则进入下一层递归全局变量会发生改变
		
		//假设特殊点在左上角区域
		if (specialROW < leftROW + subSize && specialCOL < leftCOL + subSize) {
			setBoard(specialROW, specialCOL, leftROW, leftCOL, subSize);
		}
		else {
			//不在左上角,设左上角矩阵的右下角就是特殊点(和别的一起放置L形)
			board[leftROW + subSize - 1][leftCOL + subSize - 1] = n;
			setBoard(leftROW + subSize - 1, leftCOL + subSize - 1, leftROW, leftCOL, subSize);
		}
		
		//假设特殊点在右上方
		if (specialROW < leftROW + subSize && specialCOL >= leftCOL + subSize) {
			setBoard(specialROW, specialCOL, leftROW, leftCOL + subSize, subSize);
		}
		else {
			//不在右上方,设右上方矩阵的左下角就是特殊点(和别的一起放置L形)
			board[leftROW + subSize -1][leftCOL + subSize] = n;
			setBoard(leftROW + subSize -1, leftCOL + subSize, leftROW, leftCOL + subSize, subSize);
		}
		
		//特殊点在左下方
		if (specialROW >= leftROW + subSize && specialCOL < leftCOL + subSize) {
			setBoard(specialROW, specialCOL, leftROW + subSize, leftCOL, subSize);
		}
		else {
			//不在左下方,设左下方矩阵的右上角就是特殊点(和别的一起放置L形)
			board[leftROW + subSize][leftCOL + subSize - 1] = n;
			setBoard(leftROW + subSize, leftCOL + subSize - 1, leftROW + subSize, leftCOL, subSize);
		}

		//特殊点在右下角
		if (specialROW >= leftROW + subSize && specialCOL >= leftCOL + subSize) {
			setBoard(specialROW, specialCOL, leftROW + subSize, leftCOL + subSize, subSize);
		}
		else {
			//不在右下角,设右下角矩阵的左上就是特殊点(和别的一起放置L形)
			board[leftROW + subSize][leftCOL + subSize] = n;
			setBoard(leftROW + subSize, leftCOL + subSize, leftROW + subSize, leftCOL + subSize, subSize);			
		}	
	}
	
 	
 	public void printBoard(int specialRow,int specialCol,int size) {
 		setBoard(specialRow, specialCol, 0, 0, size);
 		for (int i = 0; i < board.length; i++) {
			for (int j = 0; j < board.length; j++) {
				System.out.print(board[i][j] + " ");				
			}
			System.out.println();
		}
 	}
 	
 	
	public static void main(String[] args) {
		int N = 4;
		int specialRow = 0;
		int specialCol = 1;
		ChessProblem chessProblem = new ChessProblem(specialRow , specialCol , N);
		chessProblem.printBoard(specialRow, specialCol, N);
	}
}

这只是分治算法中的一个比较典型的问题,包括二分法查找、合并排序,快速排序等,都是运用了分治法的思想。

这次主要是通过棋盘覆盖问题看分治法的内容。



Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325591592&siteId=291194637