【问题描述】

在一个 2 ^k × 2 ^k 个方格组成的棋盘中,若有一个方格与其他方格不同,则称该方格为一特殊方格,且称该棋盘为一个特殊棋盘.显然特殊方格在棋盘上出现的位置有4^k 种情形.因而对任何k≥0,有4^k种不同的特殊棋盘.

下图中的特殊棋盘是当k=3时64个特殊棋盘中的一个:

在棋盘覆盖问题中,要用下图中 4 中不同形态的** L 型骨牌覆盖一个给定的特殊棋牌上除特殊方格以外的所有方格,且任何 2 个 L 型骨牌不得重叠覆盖**。易知,在任何一个 2^k × 2^k 的棋盘中,用到的 L 型骨牌个数恰为 (4^k-1)/3 。


用分治策略,可以设计解棋盘问题的一个简捷的算法。

当 k>0 时,将 2^k * 2^k 棋盘分割为 4 个 2^(k-1) * 2^(k-1) 子棋盘,如下图所示:

特殊方格必位于 4 个较小子棋盘之一中,其余 3 个子棋盘中无特殊方格。

为了将这 3 个无特殊方格的子棋盘转化为特殊棋盘,我们可以用一个 L 型骨牌覆盖这 3 个较小的棋盘的汇合处,如下图所示,这 3 个子棋盘上被 L 型骨牌覆盖的方格就成为该棋盘上的特殊方格,从而将原问题化为 4 个较小规模的棋盘覆盖问题。
递归的使用这种分割,直至棋盘简化为 1x1 棋盘。

【算法实现】

下面讨论棋盘覆盖问题中数据结构的设计:

(1)棋盘:可以用一个二维数组board[size][size]表示一个棋盘,其中,size=2^k。为了在递归处理的过程中使用同一个棋盘,将数组board设为全局变量;
(2)子棋盘:整个棋盘用二维数组board[size][size]表示,其中的子棋盘由棋盘左上角的下标tr、tc和棋盘大小s表示;
(3)特殊方格:用board[dr][dc]表示特殊方格,dr和dc是该特殊方格在二维数组board中的下标;
(4) L型骨牌:一个2k×2k的棋盘中有一个特殊方格,所以,用到L型骨牌的个数为(4^k-1)/3,将所有L型骨牌从1开始连续编号,用一个全局变量t表示。

【算法分析】
设T(k)是算法ChessBoard覆盖一个2k×2k棋盘所需时间,从算法的划分策略可知,T(k)满足如下递推式:

T(k) = 1             当k=0时
T(k) = 4 * T(k-1)    当k>0时

解此递推式可得 T(k) = O(4^k)。

C++代码1

#include<iostream>
using namespace std;
int tile=1;                   //L型骨牌的编号(递增)
int board[100][100];  //棋盘
/*****************************************************
* 递归方式实现棋盘覆盖算法
* 输入参数:
* tr--当前棋盘左上角的行号
* tc--当前棋盘左上角的列号
* dr--当前特殊方格所在的行号
* dc--当前特殊方格所在的列号
* size:当前棋盘的:2^k
*****************************************************/
void chessBoard ( int tr, int tc, int dr, int dc, int size )
{  if ( size==1 )    //棋盘方格大小为1,说明递归到最里层  return;  int t=tile++;     //每次递增1  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 main()
{  int size;  cout<<"输入棋盘的size(大小必须是2的n次幂): ";  cin>>size;  int index_x,index_y;  cout<<"输入特殊方格位置的坐标: ";  cin>>index_x>>index_y;  chessBoard ( 0,0,index_x,index_y,size );  for ( int i=0; i<size; i++ )  {  for ( int j=0; j<size; j++ )  cout<<board[i][j]<<"/t";  cout<<endl;  }
}

C++代码2

#include<iostream>
#include<vector>
#include<stack>  using namespace std;  vector<vector<int> > board(4);//棋盘数组,也可以作为参数传递进chessBoard中去,作为全局变量可以减少参数传递
stack<int> stI;   //记录当前所使用的骨牌号码,使用栈顶元素填充棋盘数组
int sL = 0;     //L型骨牌序号  //所有下标皆为0开始的C C++下标
void chessBoard(int uRow, int lCol, int specPosR, int specPosC, int rowSize)
{  if(rowSize ==1) return;  //static int sL = 0;棋牌和骨牌都可以用static代替,如果不喜欢用全局变量的话。  sL++;     stI.push(sL); //每递归深入一层,就把一个骨牌序号入栈  int halfSize = rowSize/2;//拆分  //注意:下面四个if else,肯定是只有一个if成立,然后执行if句,而肯定有三个else语句要执行的,因为肯定有一个是特殊位置,而其他三个是空白位置,需要填充骨牌。  //1如果特殊位置在左上角区域,则继续递归,直到剩下一个格子,并且该格子已经填充,遇到函数头一句if(rowSize == 1) return;就跳出一层递归。  //注意是一个区域或子棋盘,有一个或者多个格子,并不是就指一个格子。  if(specPosR<uRow+halfSize && specPosC<lCol+halfSize)  chessBoard(uRow, lCol, specPosR, specPosC, halfSize);  //如果其他情况  else {  board[uRow+halfSize-1][lCol+halfSize-1] = stI.top();  //因为特殊位置不在,所以可以选择任意一个空格填充,但是本算法只填充左上角(也许不止一个格,也许有很多个格子)区域的右下角。大家仔细查一下,就知道下标[uRow+halfSize-1][lCol+halfSize-1]是本区域中最右下角的一个格子的下标号。  chessBoard(uRow, lCol, uRow+halfSize-1, lCol+halfSize-1, halfSize);  //然后是递归填充这个区域的其他空白格子。因为上一句已经填充了[uRow+halfSize-1][lCol+halfSize-1]这个格子,所以,这个下标作为特殊位置参数传递进chessBoard中。  }     //2右上角区域,解析类上  if(specPosR<uRow+halfSize && specPosC>=lCol+halfSize)  chessBoard(uRow, lCol+halfSize, specPosR, specPosC, halfSize);  else {  board[uRow+halfSize-1][lCol+halfSize] = stI.top();  chessBoard(uRow, lCol+halfSize, uRow+halfSize-1, lCol+halfSize, halfSize);  }         //3左下角区域,类上  if(specPosR>=uRow+halfSize && specPosC<lCol+halfSize)  chessBoard(uRow+halfSize, lCol, specPosR, specPosC, halfSize);  else {  board[uRow+halfSize][lCol+halfSize-1] = stI.top();  chessBoard(uRow+halfSize, lCol, uRow+halfSize, lCol+halfSize-1, halfSize);  }     //4右下角区域,类上  if(specPosR>=uRow+halfSize && specPosC>=lCol+halfSize)  chessBoard(uRow+halfSize, lCol+halfSize, specPosR, specPosC, halfSize);  else {  board[uRow+halfSize][lCol+halfSize] = stI.top();  chessBoard(uRow+halfSize, lCol+halfSize, uRow+halfSize, lCol+halfSize, halfSize);  }     stI.pop();//本次骨牌号填充了三个格,填充完就出栈
}  void test()
{  //初始化数组  for(int i=0; i<4; i++)  {  board[i].resize(4);  }  chessBoard(0, 0, 3, 3, 4);  //特殊位置填充0  board[3][3] = 0;  //序列输出  for(int j=0; j<4; j++)  {  for(int i=0; i<4; i++)  cout<<board[j][i]<<"\t";  cout<<endl;  }  cout<<endl;
}  int main()
{  test();  return 0;
}

参考自:zhwhong

棋盘覆盖问题——详解(C++)相关推荐

  1. 棋盘覆盖问题详解(递归)

    棋盘覆盖问题详解(分治,非递归) (代码由 java 编写) 1.问题描述 如图(a)所示,k>0时,有一个2k×2k的棋盘,棋盘中任意位置有一个特殊的方格,要求利用图(b)中的4中L型骨牌覆盖 ...

  2. java方法不可覆盖_详解Java构造方法为什么不能覆盖,我的钻牛角尖病又犯了.......

    一 看Think in Java,遇到个程序 classEgg2 {protected classYolk {publicYolk() { System.out.println("Egg2. ...

  3. Python学习(类的属性、继承、覆盖等详解)

    Tips:如果是从基础部分一路跟着过来的朋友,需要告诉你们的是我偷偷吧编程工具PyCharm装上了,今天算是进入面向对象编程环节.本人使用的编程工具是PyCharm,未安装的可以先去官网下载安装.下面 ...

  4. mysql覆盖索引详解

    如果一个索引包含(或覆盖)所有需要查询的字段的值,称为'覆盖索引'.即只需扫描索引而无须回表. 只扫描索引而无需回表的优点: 1.索引条目通常远小于数据行大小,只需要读取索引,则mysql会极大地减少 ...

  5. 【算法】棋盘覆盖详解,基础教程~

    棋盘覆盖分析与实现 一.什么是棋盘覆盖? 在一个 2^k * 2^k 个方格组成的棋盘中,若恰有一个方格与其他方格不同,则称该方格为一个特殊方格,且称该棋盘为一个特殊棋盘.显然,特殊方格在棋盘上出现的 ...

  6. echart的关系图高亮_echarts鼠标覆盖高亮显示节点及关系名称详解

    js代码,使用了jquery和echarts.js,用的是源代码那版,因为待会要进源代码里修改-- 其实js代码和echart官网demo的代码完全没区别-- 普通的力图设置,只要加上focusNod ...

  7. python代码覆盖率测试_unittest+coverage单元测试代码覆盖操作实例详解_python

    这篇文章主要为大家详细介绍了unittest+coverage单元测试代码覆盖操作的实例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 基于上一篇文章,这篇文章是关于使用coverage来实现代码 ...

  8. linux解压zip覆盖目录,linux下压缩与解压(zip、unzip、tar)详解

    最近经常在linux上进行打包压缩解压,从网上收集整理并结合自己的常用的,跟大家分享一下下,废话不多说,进入正题. 1.zip 压缩 如果是直接压缩几个文件,那就可以直接使用命令 zip newfil ...

  9. 【算法专题】数独问题详解

    数独问题详解 1. 概述 根据百度百科的描述:数独(shù dú)是源自18世纪瑞士的一种数学游戏.是一种运用纸.笔进行演算的逻辑游戏.玩家需要根据9×9盘面上的已知数字,推理出所有剩余空格的数字,并 ...

最新文章

  1. Linux系统环境查看已经登录用户信息及管理
  2. C/C++ struct 区别
  3. 关键七步,用Apache Spark构建实时分析Dashboard
  4. typescirpt 知识点
  5. Java动态代理生成的对象导出方法
  6. 管道无损检测python_武汉哪里有便携式X射线探伤机使用方法欢迎咨询
  7. Nginx+Tomcat+Memcached实现tomcat集群和session共享
  8. leetcode328 奇偶链表
  9. 数据结构与算法分析(六)——C++实现二叉查找树
  10. java如何对foo bar调用方法_关于java:如何测试工厂方法传递给构造函数的参数?...
  11. 说说a标签的onclick和href
  12. MySql之增删改查总结
  13. 智能算法-模拟退火-粒子群-鱼群算法
  14. 阿里云盘来袭,送几个福利码!手慢无!
  15. 基本共射放大电路的简单进化 阻容式耦合放大电路
  16. django系列9 --- 迁移相关
  17. 关于爱情·萧伯纳名言大全
  18. IoT-Fast支持C#啦!教你对接HslCommunication
  19. MacBook不断重启的 5 个原因以及如何解决此问题
  20. 电商销量预测方法综述

热门文章

  1. exadata的infiniband交换机的ilom
  2. 胡凡算法之——全排列问题
  3. 学会Redis缓存中间件,这一篇就够了
  4. 记录一个使用imgkit库转图片在windows上可能会出现的问题 iis OSError: [WinError 6] 句柄无效
  5. 解决VUE项目更新后需要客户手动刷新浏览器问题
  6. Limited-Memory Quasi-Newton Methods
  7. 最简单的python语言实现汉诺塔游戏
  8. 哔哩哔哩(B站)暑期实习面经(已OC)
  9. java版本电子招标采购系统源码—企业战略布局下的采购
  10. 如何配置海康联网网关上级域,通过国标GB28181级联到EasyCVR?