一、棋盘覆盖问题
0. 特殊方格与特殊棋盘
- 在一个2k * 2k个方格组成的棋盘中,若恰有一个方格与其他方格不同,则称该方格为一特殊方格,且称该棋盘为一特殊棋盘。
- 显然,特殊方格在棋盘上出现的位置有4k种情形。因而对任何 k >= 0,有4k种特殊棋盘。
- 图1中的特殊棋盘是k=2时16个特殊棋盘中的一个。
1. 问题描述
- 在棋盘覆盖问题中,要用图2所示的4种不同形态的L型骨牌覆盖一个给定的特殊棋盘上除特殊方格以外的所有方格,且任何2个L型骨牌不得重叠覆盖。易知,在任何一个2k * 2k的棋盘覆盖中,用到的L型骨牌个数恰为(4k-1)/3。
2. 基本思想
- 用分治策略,可以设计解棋盘覆盖问题的一个简捷的算法。
- 当k>0时,将2k * 2k棋盘分割为4个2k-1 * 2k-1子棋盘,如图3-a所示。特殊方格必位于4个较小子棋盘之一中,其余3个子棋盘中无特殊方格。
- 为了将这3个无特殊方格的子棋盘转化为特殊棋盘,可以用一个L型骨牌覆盖这3个较小棋盘的会合处,如图3-b所示,这3个子棋盘上被L型骨牌覆盖的方格就成为该棋盘上的特殊方格,从而将原问题转化为4个较小规模的棋盘覆盖问题。
- 递归地使用这种分割,直至棋盘简化为1x1棋盘。
二、分治算法实现
1. 编写程序代码
//【分治】棋盘覆盖问题
#include <iostream>
using namespace std;
#define N 4 // 棋盘行(列)数
int tile = 1; // 骨牌编号
int Board[N][N];// 棋盘
void ChessBoard(int tr, int tc, int dr, int dc, int size);
int main()
{
cout << "棋盘覆盖问题,k=2时:\n\n";
// 将数组(棋盘)所有元素设为0
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
Board[i][j] = 0;
// 棋盘覆盖
ChessBoard(0, 0, 2, 3, N);
// 打印棋盘覆盖情况
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
cout << Board[i][j] << " ";
cout << endl;
}
}
/*
tr : 棋盘左上角的行号;
tc : 棋盘左上角的列号;
dr : 特殊方格左上角的行号;
dc : 特殊方格左上角的列号;
size :size = 2^k 棋盘规格为2^k*2^k
*/
void ChessBoard(int tr, int tc, int dr, int dc, int size)
{
if (size == 1)
return;
int t = tile++; // L型骨牌编号
int s = size / 2; // 分割棋盘
// 覆盖左上角子棋盘
// 特殊方格在此棋盘中(下面三个if-else同理)
if (dr < tr + s && dc < tc + s)
ChessBoard(tr, tc, dr, dc, s);
// 此棋盘中无特殊方格(下面三个if-else同理)
else
{
Board[tr + s - 1][tc + s - 1] = t; // 用编号为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; // 用编号为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; // 用编号为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; // 用编号为t的骨牌覆盖左上角
ChessBoard(tr + s, tc + s, tr + s, tc + s, s); //覆盖其余方格
}
}
2. 运行结果展示
3. 程序改进
上面的程序是以k=2为例的棋盘覆盖,也可以cin获取用户输入~
三、友情链接~
- 其它一些常见算法请参阅此链接~
最后,非常欢迎大家来讨论指正哦!