分治法之棋盘覆盖问题

写此博文目的:

1.刚学了棋盘覆盖问题,自己实现它,加深自己的理解很感悟

2.给为棋盘问题困惑的朋友带来一点思路

 

开始分析!

 

什么叫做分治法呢?

:简单来说就是分而治之,先把问题分解成很多个小问题,然后再处理它

棋盘覆盖问题就是一个很经典的分治问题

首先我们先来看一下棋盘覆盖问题到底是个什么问题?

题目引用自:https://blog.csdn.net/acm_jl/article/details/50938164

思路分析:

将一个大的棋盘划分为相同大小的四块,在这四块相同大小的子棋盘中,现在只有一个子棋盘有一个格子是不可覆盖的,还有三个子棋盘是所有的格子都是可以覆盖的,所以我们需要为这3个不存在不可覆盖格子的子棋盘构造3个不可覆盖的格子,那么我们应该如何构造呢?

下面我们看一张图:

假如现在第一个不可覆盖的格子在左上角,那么我们需要构造的3个不可覆盖的格子就跟上面的图一样,构造的这3个不可覆盖的格子的连接成的形状肯定是个L型,只是会随着棋盘中不可覆盖的格子的位置的不同而L型的开口方向会有所变化

总结一下:

2k2k2k∗2k的棋盘划分为2k12k12k−1∗2k−1这样的子棋盘4块。递归填充各个格子,填充分为四个情况,归出口为s=0,s=0也就是子棋盘方格数为1。

递归的四种情况:

如果黑方块在左上子棋盘,则递归填充左上子棋盘;否则填充左上子棋盘的右下角,将右下角看做黑色方块,然后递归填充左上子棋盘。

如果黑方块在右上子棋盘,则递归填充右上子棋盘;否则填充右上子棋盘的左下角,将左下角看做黑色方块,然后递归填充右上子棋盘。

如果黑方块在左下子棋盘,则递归填充左下子棋盘;否则填充左下子棋盘的右上角,将右上角看做黑色方块,然后递归填充左下子棋盘。

如果黑方块在右下子棋盘,则递归填充右下子棋盘;否则填充右下子棋盘的右下角,将左上角看做黑色方块,然后递归填充右下子棋盘。

好啦,话不多说,我们直接撸代码吧!!!

 1 #include<stdio.h>
 2 #define max 1024
 3 int cb[max][max];//最大棋盘
 4 int id=0;//覆盖标志位
 5 int chessboard(int tr,int tc,int dr,int dc,int size)//tr,tc代表棋盘左上角的位置,dr ,dc代表棋盘不可覆盖点的位置,size是棋盘大小
 6 {
 7     if(size==1)//如果递归到某个时候,棋盘大小为1,则结束递归
 8     {
 9         return 0;
10     }
11     int s=size/2;//使得新得到的棋盘为原来棋盘大小的四分之一
12     int t=id++;
13     if(dr<tr+s&&dc<tc+s)//如果不可覆盖点在左上角,就对这个棋盘左上角的四分之一重新进行棋盘覆盖
14     {
15         chessboard(tr,tc,dr,dc,s);
16     }else//因为不可覆盖点不在左上角,所以我们要在左上角构造一个不可覆盖点
17     {
18         cb[tr+s-1][tc+s-1]=t;//构造完毕
19         chessboard(tr,tc,tr+s-1,tc+s-1,s);//在我们构造完不可覆盖点之后,棋盘的左上角的四分之一又有了不可覆盖点,所以就对左上角棋盘的四分之一进行棋盘覆盖
20     }
21 
22     if(dr<tr+s&&dc>=tc+s)//如果不可覆盖点在右上角,就对这个棋盘右上角的四分之一重新进行棋盘覆盖
23     {
24         chessboard(tr,tc+s,dr,dc,s);
25     }else//因为不可覆盖点不在右上角,所以我们要在右上角构造一个不可覆盖点
26     {
27         cb[tr+s-1][tc+s]=t;
28         chessboard(tr,tc+s,tr+s-1,tc+s,s);//在我们构造完不可覆盖点之后,棋盘的右上角的四分之一又有了不可覆盖点,所以就对右上角棋盘的四分之一进行棋盘覆盖
29     }
30 
31 
32      if(dr>=tr+s&&dc<tc+s)//如果不可覆盖点在左下角,就对这个棋盘左下角的四分之一重新进行棋盘覆盖
33     {
34         chessboard(tr+s,tc,dr,dc,s);
35     }else//因为不可覆盖点不在左下角,所以我们要在左下角构造一个不可覆盖点
36     {
37         cb[tr+s][tc+s-1]=t;
38         chessboard(tr+s,tc,tr+s,tc+s-1,s);//在我们构造完不可覆盖点之后,棋盘的左下角的四分之一又有了不可覆盖点,所以就对左下角棋盘的四分之一进行棋盘覆盖
39     }
40 
41     if(dr>=tr+s&&dc>=tc+s)//如果不可覆盖点在右下角,就对这个棋盘右下角的四分之一重新进行棋盘覆盖
42     {
43         chessboard(tr+s,tc+s,dr,dc,s);
44     }else//因为不可覆盖点不在右下角,所以我们要在右下角构造一个不可覆盖点
45     {
46         cb[tr+s][tc+s]=t;
47         chessboard(tr+s,tc+s,tr+s,tc+s,s);//在我们构造完不可覆盖点之后,棋盘的右下角的四分之一又有了不可覆盖点,所以就对右下角棋盘的四分之一进行棋盘覆盖
48     }
49 
50     //后面的四个步骤都跟第一个类似
51 }
52 int main()
53 {
54     printf("请输入正方形棋盘的大小(行数):\n");
55     int n;
56     scanf("%d",&n);
57     printf("请输入在%d*%d棋盘上不可覆盖点的位置:\n",n,n);
58     int i,j,k,l;
59     scanf("%d %d",&i,&j);
60     printf("不可覆盖点位置输入完毕,不可覆盖点的值为-1\n");
61     cb[i][j]=-1;
62     chessboard(0,0,i,j,n);
63     for(k=0;k<n;k++)
64     {
65         printf("%2d",cb[k][0]);
66         for(l=1;l<n;l++)
67         {
68             printf(" %2d",cb[k][l]);
69         }
70         printf("\n");
71     }
72     return 0;
73 }

代码分析:

我们先看chessboard函数

函数的主干是四个if else循环,也就是说只会执行一个if中的语句,但是会执行3个else中的语句,这3个else中的语句就是构造不可覆盖格子,然后对含有新构造的不可覆盖点的子棋盘来重写进行棋盘覆盖,也就是递归调用棋盘覆盖函数,递归的结束条件就是子棋盘只有一个格子,也就是s=1,每次调用棋盘覆盖函数,都要进行s=size/2,目的就是把一个大棋盘划分为四个相同大小的子棋盘。

运行结果:

 

不足错误的地方,欢迎各位拍砖指正哦!!!!!

技术在于分享

posted @ 2018-03-28 21:24  西*风  阅读(8258)  评论(1编辑  收藏  举报