其实就是算法设计课上的例题,然后我找到OJ上的题测试一下。

传送门: 洛谷 U91193 棋盘覆盖

细节挺多的,我主要卡的地方就是计数。计数的时候注意要先用一个变量存骨牌编号,因为之后再递归编号就增加了,而实际上返回本层的时候应该填入的是原来的编号。

还有就是题面有点问题,矩阵得开到1000*1000,否则RE。

总体就是分治的思想,具体实现见代码注释。

#include <bits/stdc++.h>
using namespace std;
const int N=1e3+10;
int k,n,bx,by,tot,ans[N][N];
// L形骨牌编号tot
void solve(int x1,int y1,int x2,int y2,int sz)
// 当前棋盘左上角(x1,y1)
// 当前棋盘中特殊方格位置(x2,y2)
// 当前棋盘大小sz*sz
{
    if(sz==1)return;
    int t=++tot; // 这里一定要用一个变量把tot先记下来,否则计数会出错!
    //因为tot继续递归,将会增加,返回到本层的时候实际上要填的数应该是原来的tot
    sz/=2;// 缩小棋盘
    int midx=x1+sz-1;
    int midy=y1+sz-1;
    // 当前棋盘的中心靠左上位置(midx,midy)

    // 1
    if(x2<=midx&&y2<=midy) // 特殊方格在左上部分,继续划分
    {
        solve(x1,y1,x2,y2,sz);
    }
    else
    {
        ans[midx][midy]=t; // 不在左上,覆盖左上部分的右下角
        solve(x1,y1,midx,midy,sz); // 继续划分
    }

    // 2
    if(x2<=midx&&y2>midy) // 特殊方格在右上部分,继续划分
    {
        solve(x1,y1+sz,x2,y2,sz);
    }
    else
    {
        ans[midx][midy+1]=t; // 不在右上,覆盖右上部分的左下角
        solve(x1,y1+sz,midx,midy+1,sz); // 继续划分
    }

    // 3
    if(x2>midx&&y2<=midy) // 特殊方格在左下部分,继续划分
    {
        solve(x1+sz,y1,x2,y2,sz);
    }
    else
    {
        ans[midx+1][midy]=t; // 不在左下,覆盖左下部分的右上角
        solve(x1+sz,y1,midx+1,midy,sz); // 继续划分
    }

    // 4
    if(x2>midx&&y2>midy) // 特殊方格在右下部分,继续划分
    {
        solve(x1+sz,y1+sz,x2,y2,sz);
    }
    else
    {
        ans[midx+1][midy+1]=t; // 不在右下,覆盖右下部分的左上角
        solve(x1+sz,y1+sz,midx+1,midy+1,sz); // 继续划分
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin>>k>>bx>>by;
    n=(1<<k); //2^k
    solve(1,1,bx,by,n);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            j==n?printf("%d\n",ans[i][j]):printf("%d ",ans[i][j]);
    return 0;
}
/*
3
1 1
ans:
 0  3  4  4  8  8  9  9
 3  3  2  4  8  7  7  9
 5  2  2  6 10 10  7 11
 5  5  6  6  1 10 11 11
13 13 14  1  1 18 19 19
13 12 14 14 18 18 17 19
15 12 12 16 20 17 17 21
15 15 16 16 20 20 21 21
*/

具体的手工模拟过程可参考此文:棋盘覆盖问题-分治法