棋盘覆盖问题(C++分治思想强化)

本篇主要内容是叙述我思考棋盘覆盖问题的心路历程和最终解决。

今天花了1个多小时写棋盘覆盖问题,然后写出来的代码是一坨@_@

索性看了题解代码,结果发现还是看不懂,就去看了讲解视频。基本思路是理解了。

然后又花了2个多小时自己写思路和代码,终于运行通过了。

现在,我在此重新回顾总结一下棋盘覆盖问题给我带来的思考。

————————————————————

先把题目拿出来看看:

先说说我原来写的错误的且未完成的代码,如下:

#include <cstdio>

#include <cmath>

const int MAXNUM = 300;

bool a[MAXNUM][MAXNUM] = {false};

int corn[MAXNUM][2];

int outLine;

int idx = 0;

bool cover1(int i, int j){

    if(a[i][j] == false && a[i][j+1] == false && a[i+1][j+1] == false){

    return true;

    }else{

        return false;

    }

}

void _cover1(){

    a[i][j] = true;

    a[i][j+1] = true;

    a[i+1][j+1] = true;

    corn[idx][0] = i;

    corn[idx][1] = j+1;

}

bool cover2(int i, int j){

    if(a[i][j] == false && a[i+1][j] == false && a[i+1][j-1] == false){

    return true;

    }else{

        return false;

    }

}

void _cover2(){

    a[i][j] = true;

    a[i+1][j] = true;

    a[i+1][j-1] = true;

    corn[idx][0] = i+1;

    corn[idx][1] = j;

}

bool cover3(int i, int j){

    if(a[i][j] == false && a[i][j+1] == false && a[i+1][j] == false){

    a[i][j] = true;

    a[i][j+1] = true;

    a[i+1][j] = true;

    return true;

    }else{

        return false;

    }

}

void _cover3(){

    a[i][j] = true;

    a[i][j+1] = true;

    a[i+1][j] = true;

    corn[idx][0] = i;

    corn[idx][1] = j;

}

bool cover4(int i, int j){

    if(a[i][j] == false && a[i+1][j] == false && a[i+1][j+1] == false){

    return true;

    }else{

        return false;

    }

}

void _cover4(){

    a[i][j] = true;

    a[i+1][j] = true;

    a[i+][j+1] = true;

    corn[idx][0] = i+1;

    corn[idx][1] = j;

}

bool comple = false;

int rowIdx = 1,colIdx = 1;

bool isComple(){

    comple = true;

    for(rowIdx = 1; rowIdx < outLine; rowIdx++){

        for(colIdx = 1; colIdx < outLine; colIdx++){

            if(!a[colIdx][colIdx]){

                comple = false;

                return false;

            }

        }

    }

    if(comple) return true;

}

void getSolution(){

//还不知道我要写啥

}

int main(){

    int k,cx,cy;

    scanf("%d%d%d",&k,&cx,&cy);

    a[cx][cy] = true;

    outLine = (int)pow(2.0,(double)k) + 1;

    for(int i=0; i<= outLine; i++){

        a[0][i] = true;

        a[i][0] = true;

        a[outLine][i] = true;

        a[i][outLine] = true;

    }

    getSolution();

    return 0;

}

我一开始的思路吧,是暴力解决,想把所以覆盖方法走一遍。把棋盘方格用一个二维数组表达出来,然后按x从小到大,y从下到大的顺序依次把方格用四种骨牌覆盖方法覆盖,再把已覆盖的方格的二维数组对应值设为true,但是我发现这种方式压根无法推进,也无法判断怎么覆盖一定有解。总之,行不通...

下面我们来思考如何使用分治思想解决这道题。分治,分治,即分而治之。先思考一下2^(k+1) * 2^(k+1)棋盘和2^k * 2^k棋盘有啥关系。没错,2^(k+1) * 2^(k+1)棋盘可以分割为四个2^k * 2^k棋盘,换言之,2^(k+1) * 2^(k+1)棋盘中有四个2^k * 2^k棋盘。如果要将该问题的规模缩小,也就是说要将该问题分解,那么我们将原规模的问题分解出规模更小的原问题。分解出规模更小的原问题过程如下:

 由上面过程我们就得到了递归式和递归边界。那么,如何来书写代码呢?

首先,我们考虑输入和输出所需的数据结构类型。

通过上面的分析,可知,较优的方案是输入由三个int型变量存储,输出用一个结构体数组存储。

有了这些分析后,我们就知道了写代码的关键就是定义这些变量然后实现递归函数。

这里写递归函数的时候,我又犯了两个致命的错误,先不说,看看代码:

 

第一个致命的错误就是我一开始忽略了上图上的紫色部分代码,即递归边界

第二个致命的错误就是我的递归函数和结构体数组中的坐标值写错了!!!(注意,坐标一定是相对坐标,其实所谓的绝对坐标也是相对坐标,只不过相对起始点为原点罢了。所以我们面对二维或三维空间确定坐标时一定要思考我们是相对哪个起始点或者说相对哪些坐标轴的坐标,然后再加上距离计算出相对坐标。这里我直接用距离来表示坐标了,而忽略了相对坐标起始点,就错了

下面是正确运行的代码:

#include <cstdio>

#include <cmath> //pow

#include <algorithm> //sort

using namespace std;

const int MAXNUM = (256*256-1)/3;

int num = 0;

struct Point{

    int x;

    int y;

    Point(){}

    Point(int _x,int _y){

        x = _x;

        y = _y;

    }

}card[MAXNUM];

bool cmp(Point a, Point b){

    if(a.x != b.x){

        return a.x < b.x;

    }else{

        return a.y < b.y;

    }

}

void cover(int firx,int firy, int cx, int cy, int n){ //firx意为first-x,firy意为first-y,cx,cy为特殊方格的横纵坐标,n为棋盘边长

    if(n == 1) return;

    int half = n / 2;

    if(cx <= firx-1+ half && cy <= firy-1+ half){

        card[num++] = Point(firx-1+half+1,firy-1+half+1);

        cover(firx,firy,cx,cy,half);

    }else{

        cover(firx,firy,firx-1+half,firy-1+half,half);

    }//左下

    if(cx > firx-1+ half && cy <= firy-1+ half){

        card[num++] = Point(firx-1+half,firy-1+half+1);

        cover(firx-1+half+1,firy,cx,cy,half);

    }else{

        cover(firx-1+half+1,firy,firx-1+half+1,firy-1+half,half);

    }//右下

    if(cx <= firx-1+ half && cy > firy-1+ half){

        card[num++] = Point(firx-1+half+1,firy-1+half);

        cover(firx,firy-1+half+1,cx,cy,half);

    }else{

        cover(firx,firy-1+half+1,firx-1+half,firy-1+half+1,half);

    }//左上

    if(cx > firx-1+ half && cy > firy-1+ half){

        card[num++] = Point(firx-1+half,firy-1+half);

        cover(firx-1+half+1,firy-1+half+1,cx,cy,half);

    }else{

        cover(firx-1+half+1,firy-1+half+1,firx-1+half+1,firy-1+half+1,half);

    }//右上

}

int main(){

    int k,cx,cy;

    scanf("%d%d%d",&k,&cx,&cy);

    int n = (int)pow(2.0,(double)k);

    cover(1,1,cx,cy,n);

    sort(card,card + num,cmp);

    for(int i=0; i<num; i++){

        printf("%d %d\n",card[i].x,card[i].y);

    }

    return 0;

}

最后总结一下解决该题的收获以结束此篇:

1.需要排序时避免使用二维数组,用二维数组实现可以改为结构体数组实现,便于用sort函数排序

2.分治思想解决问题的关键在于将原问题分解出规模减小了的原问题

3.递归函数一定要具有两个要素1.递归式 2.递归边界。(缺一不可)

4.坐标一定是相对坐标,确定坐标时候只需确定两个要素:1.相对点 2.和相对点之间的横纵距离

棋盘覆盖问题是指在一个大小为2^n * 2^n的棋盘上去掉一个方格后,用L型骨牌(形状类似“日”字)将其覆盖。可以使用可视化工具来演示这个问题的解决过程。以下是一个简单的Python示例代码: ```python import numpy as np import matplotlib.pyplot as plt def plot_board(board): # 绘制棋盘 plt.imshow(board, cmap='binary') plt.xticks([]) plt.yticks([]) def plot_domino(x, y, c): # 绘制L型骨牌 xs = [x, x+1, x+1, x] ys = [y, y, y+1, y+1] plt.fill(xs, ys, c) def cover(board, lab, top, left, side): # 递归解决棋盘覆盖问题 if side == 1: return s = side // 2 t = lab lab += 1 mid = top + s - 1 if x < mid and y < mid: cover(board, lab, top, left, s) plot_domino(left+s-1, top+s-1, 'r') board[mid][mid-1] = board[mid-1][mid] = lab plot_domino(left+s-1, top+s, 'b') cover(board, lab, top, left+s, s) plot_domino(left+s, top+s-1, 'b') cover(board, lab, top+s, left, s) plot_domino(left+s, top+s, 'r') cover(board, lab, top+s, left+s, s) elif x < mid and y >= mid: cover(board, lab, top, left+s, s) plot_domino(left+s, top+s-1, 'b') board[mid-1][mid-1] = board[mid][mid] = lab plot_domino(left+s-1, top+s-1, 'r') cover(board, lab, top, left, s) plot_domino(left+s-1, top+s, 'r') cover(board, lab, top+s, left, s) plot_domino(left+s, top+s, 'b') cover(board, lab, top+s, left+s, s) elif x >= mid and y < mid: cover(board, lab, top+s, left, s) plot_domino(left+s-1, top+s, 'r') board[mid-1][mid-1] = board[mid][mid] = lab plot_domino(left+s, top+s-1, 'b') cover(board, lab, top, left, s) plot_domino(left+s-1, top+s-1, 'b') cover(board, lab, top, left+s, s) plot_domino(left+s, top+s, 'r') cover(board, lab, top+s, left+s, s) else: cover(board, lab, top+s, left+s, s) plot_domino(left+s, top+s, 'r') board[mid-1][mid] = board[mid][mid-1] = lab plot_domino(left+s-1, top+s-1, 'b') cover(board, lab, top, left, s) plot_domino(left+s-1, top+s, 'b') cover(board, lab, top, left+s, s) plot_domino(left+s, top+s-1, 'r') cover(board, lab, top+s, left, s) lab = t n = 4 board = np.zeros((2**n, 2**n), dtype=int) board[3][3] = -1 # 去掉一个方格 plot_board(board) cover(board, 1, 0, 0, 2**n) plt.show() ``` 这段代码使用了matplotlib库来绘制棋盘和骨牌。首先使用`plot_board`函数绘制棋盘,然后使用`plot_domino`函数绘制L型骨牌。`cover`函数用递归的方式解决棋盘覆盖问题。在每次递归调用时,根据覆盖区域的位置和大小,决定放置哪个方向的L型骨牌,并用颜色标记不同的骨牌。最后调用`plt.show()`函数显示绘制结果。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值