代码分析+原理图解——棋盘覆盖问题-分治法

上算法课时,老师以文字+代码的方式讲了这道题,然而有很多同学反映听的不是太懂, 我们接触事物最直观的就是以图片理解,因此我尝试使用图解法来帮助大家理解。


问题描述:

注意:分治法最核心的一点是:分出的子问题要与原问题性质相同

但不管我们怎么分,只要这个奇异点在某一个子问题中, 这个子问题必定不与其他子问题相等。

如果将奇异点扣除再分,又无法等分成正方形棋盘。

因此,我们解决这道题的思路是:在没有奇异点的子棋盘中,人为的构造奇异点,使其与有奇异点的子问题相同即可。


首先给出一个包含一个奇异点的16*16棋盘:

第一步是将该棋盘分为四个等大的子棋盘:

然后将该棋盘看做是4*4的棋盘,可以看到奇异点在左上角的子棋盘中,那么这一步的任务就是用一个(真的是一个)L型的棋子(下图中红色的格子)将其他三个子棋盘构造成含奇异点的子棋盘:

下一步是将红线分割的子棋盘又切割成四个子棋盘(白色线切开的子棋盘):

然后对每个红色线包围在里面的子棋盘,用一个L型棋子(黄色)又构造出奇异点,使得每个子棋盘都有一个奇异点,即白色线围起来的格子看做是一个整体,里面包含一个黄色的奇异点:

下一步是继续讲白色线包围的格子切分为4个子棋盘,这里为了方便观察,将前面所有的分割线去掉:

同理,对黄色的棋盘构造含奇异点的子棋盘(蓝色):

最后可以分割到2*2的格子,然后每个2*2的子棋盘都已经包含一个奇异点了,剩下的就是用L型旗子去填好剩下的三个格子。


到此为止, 棋盘的分割就结束了。

那么,如何将其转化为代码实现呢?

输入n(棋盘大小)、 奇异点坐标、用不同数字代表不同的骨牌进行输出。

运行结果如下图所示:


接下来分析代码:


代码思路分析:

虽然理解了原理, 可是转化为代码的历程并非一帆风顺, 要考虑很多的细节。

心路历程:
最初是想要分别求出四种情况,判断每种情况,并赋予不同的奇异点,但发现赋予奇异点后没办法用统一的分治迭代(因为每种情况的参数不同),但若是每种情况下都去迭代四个分治,又太麻烦, 于是放弃。

搜网后, 发现主流的解法是:同样用四个if判断,但每个if判断如果成立,直接分治;如果不成立,则转到else中分治, 这样只用了四个判断,八个条件就解出了问题。大大提高了代码的复用性

注意:
不要被“棋盘”误导,棋子并不是要下到边界线上, 而是要将棋盘内空间看做点


代码展示

#include<iostream>
using namespace std;

int def[110][110];
static int t = 0;

void chess(int a, int b, int aa, int bb, int length) {
    
    
	if(length==1) return;
	t++;
	int tem = t;
	int l = length/2;
	if(aa<a+l && bb<b+l) chess(a,b,aa,bb,l);
	else {
    
    
		def[a+l-1][b+l-1] = tem;
		chess(a,b,a+l-1,b+l-1,l);
	}
	
	if(aa>=a+l && bb<b+l) chess(a+l,b,aa,bb,l);	//左下角棋盘
	else {
    
    
		def[a+l][b+l-1]=tem;
		chess(a+l,b,a+l,b+l-1,l);
	} 
	
	if(aa<a+l && bb>=b+l) chess(a,b+l,aa,bb,l);	//右上角的子棋盘
	else {
    
    
		def[a+l-1][b+l] = tem;
		chess(a,b+l,a+l-1,b+l,l);
	} 
	
	if(aa>=a+l && bb>=b+l) chess(a+l, b+l, aa, bb,l);
	else {
    
    
		def[a+l][b+l]=tem;
		chess(a+l, b+l, a+l, b+l, l);
	}
} 

int main() {
    
    
	int n, a, b, aa, bb, length, m;
	//a,b 是子棋盘左上角的行列号
	//aa,bb是特殊点的行列号
	cin>>length>>aa>>bb;
	a = b = 1;
	m = length;
	
	chess(a,b,aa,bb,length); 
	
	for(int i=1;i<=m;i++) //输出结果
        for(int j=1;j<=m;j++){
    
    
            cout.width(3);
            cout<<def[i][j];
            if(j==m) cout<<endl;
        }
	return 0;
}

如果觉得还不错,就请给博主一个赞吧!

猜你喜欢

转载自blog.csdn.net/weixin_43899069/article/details/108801860