1:引出
首先用合并排序来引出我们今天的主角,合并排序即使用了分治的策略,它将所要排序的区间划分为2个子区间,将这两个子区间分别进行递归排好顺序,然后合并这两个子区间(合并过程运用了简单的双指针算法)即获得原问题的解。其实棋盘覆盖问题也是这样的。
2:主角登场
问题:在一个2k*2k个方格组成的棋盘中,恰有一个方格与其他方格不同,称该方格为一特殊方格。在棋盘覆盖问题中,要用图示的4种不同形态的L型骨牌覆盖给定的特殊棋盘上除特殊方格以外的所有方格,且任何2个L型骨牌不得重叠覆盖。
3:解法:
1:将棋盘分为4个2k-1*2k-1的子棋盘。
2:递归地覆盖4个子棋盘。
3:合并4个子棋盘(代码不用写该过程)。
关于递归,引用该链接的想法:
链接: link.http://www.ezloo.com/2008/04/chessboard_cover.html
特殊方格一定位于四个子棋盘中的一个,构造剩下没特殊方格三个子棋盘,将他们中的也假一个方格设为特殊方格。如果是:
左上的子棋盘(若不存在特殊方格)----则将该子棋盘右下角的那个方格假设为特殊方格
右上的子棋盘(若不存在特殊方格)----则将该子棋盘左下角的那个方格假设为特殊方格
左下的子棋盘(若不存在特殊方格)----则将该子棋盘右上角的那个方格假设为特殊方格
右下的子棋盘(若不存在特殊方格)----则将该子棋盘左上角的那个方格假设为特殊方格
当然上面四种,只可能且必定只有三个成立,那三个假设的特殊方格刚好构成一个L型骨架,我们可以给它们作上相同的标记(标号)。这样四个子棋盘就分别都和原来的大棋盘类似,我们就可以用递归算法解决。
4:代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | #include<iostream> using namespace std; //(tr,tc)所要覆盖棋盘的左上角方格的坐标 //(dr,dc)所要覆盖棋盘的特殊方格的坐标 //size是所要覆盖棋盘的行宽和列宽。 const int N = 10; int title = 1; //L型骨牌的编号 int board[N][N]; //棋盘 void ChessBoard(int tr,int tc, int dr,int dc,int size) { if(size == 1) return; int t = title ++; int s = size / 2; //覆盖左上角棋盘 if (dr < tr + s && dc < tc + s) { ChessBoard(tr, tc, dr, dc, s); } else { board[tr + s -1][tc + s - 1] = t; ChessBoard(tr,tc,tr + s - 1, tc + s - 1,s); } //覆盖右上角棋盘 if(dr < tr + s && dc >= tc + s) { ChessBoard(tr, tc + s, dr, dc, s); } else { board[tr + s - 1][tc + s] = t; ChessBoard(tr, tc + s, tr + s - 1, tc + s, s); } //覆盖左下角棋盘 if (dr >= tr + s && dc < tc + s ) { ChessBoard(tr + s, tc, dr, dc, s); } else { board[tr + s][tc + s - 1] = t; ChessBoard(tr + s, tc, tr + s, tc + s - 1, s); } //覆盖右下角棋盘 if(dr >= tr + s && dc >= tc + s) { ChessBoard(tr + s, tc + s, dr, dc, s); } else { board[tr + s][tc + s] = t; ChessBoard(tr + s, tc + s, tr + s, tc + s, s); } } void ChessPrint(int size) { for (int i = 0; i < size;i++) { for (int j = 0; j < size; j++) { cout << board[i][j] << " "; } cout << endl; } } int main() { int size; //棋盘大小 cin >> size; int x, y; //特殊方格的坐标 cin >> x >> y; ChessBoard(0, 0, x, y, size); ChessPrint(size); return 0; } |
5:总结:
分治策略即将原问题划分为若干个规模较小而结构与原问题相同或相似的子问题,然后分别解决这些子问题,最后合并子问题,即可得到原问题的解。
一般性的步骤:
1:分解:将原问题划分为若干个规模较小而结构与原问题相同或相似的子问题
2:解决:递归求解所有子问题。
3:合并:将子问题的解合并为原问题的解。