前言
第一次做这题的时候毫无思路,看各路题解的时候发现大家讲解都只点到分治的思路,具体实现代码注释太少,而且变量命名确实看的头疼,故本人在两个半小时苦战后决定分享一下思路和其具体实现的方法
大体思路
题目跳转链接洛谷p1228
此处借鉴了多篇其他题解,具体如下:
紧接着是k>2的情况,设定n为边长我们先画出一个8*8的图
1 | 2 | ||||||
3 | 4 | ||||||
我们创建middlex=middley=(1+n)/2,本意是想用坐标表示最中心那个‘点’,但因为算出来带小数加上int自动舍弃掉小数位,所以实际上middlex和middley组合起来的表示的是图中标有1的格子(重要!但后续为方便阐述还是称其为中点),这个影响主要体现在后续①中大小于判断中加的等号,即大于等于或小于等于。
接着利用1这个点判定公主(为表述方便,真公主假公主统称公主)在左上左下右上右下哪个区域,假如公主在左上这个区域,那么左上角这一个正方形最后一定会被铺满,也就是说图中这个1这个格子也一定会被铺上(这里关键点就在于这四块区域如果都有一个公主,那就可以达到刚好铺满的目的了),所以只需要根据上一行说的那个一定会被铺上的格子来给中间这四格铺上一个地毯(比如刚刚说的这种情况,只需要铺上第一号地毯即可,地毯的坐标即为图中标出的4),然后把234都设置成假公主(具体实现代码注释中有写),那么就可以把问题拆成处理4个子问题了
所以我们只需要写一个solve函数,设置七个形参,分别是左上角顶点的x和y,右下角顶点的x和y(这两个点的意义就是用来求middlex和middley),对于当前正方形来说唯一(这个唯一的理解很重要,意味着任意一块只需要且只有一个公主的时候问题既可以解决)(至于这是为什么呢,其实很好理解,由题意可知边长为2^k的矩形中只要有一格站了公主,那么这一块矩形一定能刚好填满)的那个公主坐标x和y,边长(这个主要用来判定递归结束,其实不是必要的,可以用顶点的x和y来直接计算边长,但这样写也有他方便的地方就不改了),然后进行上两段所说的操作即可(即取中点,判断公主在四大块那一块,根据公主位置输出中心铺的毯子坐标和形状,把1234中的三个变成假公主,递归传递新的顶角坐标和公主坐标(真公主或者刚刚生成的假公主)和边长(自除2))
代码(以及详细注释)
#include <iostream>
using namespace std;
void solve (int zuoshangx, int zuoshangy, int youxiax, int youxiay, int gongzhux, int gongzhuy, int bianchang)//边长用来判定递归结束,形参意义同形参名称
//这里middlex和middley本来应该存中间那个点的坐标,而边长是偶数这个点的坐标必定是小数,此处需注意
{
if (bianchang == 1) return;//这个bianchang完全可以省,只需要用传进来的坐标就可以计算边长,但这样写感觉方便一丢丢就不改了
int middlex = (zuoshangx + youxiax) / 2;//这里middlex和middley的本意是想表示网格中间那个'点'的坐标,但如第四行注释所说
//小数不太好处理,所以这里表示的实际上是"中点左上角邻近的那个'格子'的坐标"(注意这句),所以下面判断公主位置的时候注意大小于的等号即可
int middley = (zuoshangy + youxiay) / 2;
if (gongzhux <= middlex && gongzhuy <= middley)//①
{
cout << middlex + 1 << ' ' << middley + 1 << ' ' << 1 << '\n';/*如果公主在中点的左上方,那中点左上方这一整块最终一定会被填满(重要!!),所以中点左上角这一块(其实就是(middlex,middley))最后一定会长出来一个新公主
接着把中点临近的这四个格中剩下的那三个填上假公主(具体体现在下面四个递归函数中的公主坐标传参)(重要!!)*/
solve (zuoshangx, zuoshangy, middlex, middley, gongzhux, gongzhuy, bianchang/2);//这四行递归从上到下是左上角,左下角,右上角,右下角,注意每一次传进去的边长都要除以2
solve (middlex+1, zuoshangy, youxiax, middley, middlex+1, middley, bianchang/2);/*递归传参的时候理解的时候要注意,所谓的左上角右下角坐标并不是指某个'点',而是某个格子的坐标,可画图理解
画图的时候想要确定某个点(如左上角那个点),可以把其x那一行画出来,把y那一列也画出来,相交的那个格子就是要找的格子(重要!!)*///这两行讲了一堆主要影响的是传参里那些奇怪的+1的来由
solve (zuoshangx, middley+1, middlex, youxiay, middlex, middley+1, bianchang/2);
solve (middlex+1, middley+1, youxiax, youxiay, middlex+1, middley+1, bianchang/2);
}
else if (gongzhux > middlex && gongzhuy <= middley)//左下角
{
cout << middlex << ' ' << middley + 1 << ' ' << 3 << '\n';
solve (zuoshangx, zuoshangy, middlex, middley, middlex, middley, bianchang/2);//其实不同if里面的四个递归区别只在于传递的公主坐标,左上角和右下角坐标是一样的
solve (middlex+1, zuoshangy, youxiax, middley, gongzhux, gongzhuy, bianchang/2);
solve (zuoshangx, middley+1, middlex, youxiay, middlex, middley+1, bianchang/2);
solve (middlex+1, middley+1, youxiax, youxiay, middlex+1, middley+1, bianchang/2);
}
else if (gongzhux <= middlex && gongzhuy > middley)//右上角
{
cout << middlex + 1 << ' ' << middley << ' ' << 2 << '\n';
solve (zuoshangx, zuoshangy, middlex, middley, middlex, middley, bianchang/2);
solve (middlex+1, zuoshangy, youxiax, middley, middlex+1, middley, bianchang/2);
solve (zuoshangx, middley+1, middlex, youxiay, gongzhux, gongzhuy, bianchang/2);
solve (middlex+1, middley+1, youxiax, youxiay, middlex+1, middley+1, bianchang/2);
}
else//右下角
{
cout << middlex << ' ' << middley << ' ' << 4 << '\n';
solve (zuoshangx, zuoshangy, middlex, middley, middlex, middley, bianchang/2);
solve (middlex+1, zuoshangy, youxiax, middley, middlex+1, middley, bianchang/2);
solve (zuoshangx, middley+1, middlex, youxiay, middlex, middley+1, bianchang/2);
solve (middlex+1, middley+1, youxiax, youxiay, gongzhux, gongzhuy, bianchang/2);
}//写到这应该发现了,四个递归里有一个传的是真公主(注意这里的真公主指的是一开始就有的那个加上本次递归之前创建的假公主),剩下三个就是根据middle的坐标传的这时候新创建的假公主的坐标(请区别这个假公主和上一个假公主)
//所以四个if中,四个递归直接复制第一个if的之后改一下传的公主坐标即可
}
int main ()
{
int k, n, gongzhux, gongzhuy;
cin >> k >> gongzhux >> gongzhuy;
n = 1 << k;//即n = 2^k,没见过可以查一下
solve (1, 1, n, n, gongzhux, gongzhuy, n);
return 0;
}
对递归函数传参的理解
定义递归函数时形参顺序的理解
首先理清递归函数定义时的形参列表与其内部递归函数的形参列表之间的联系,重点在于形参位置,或者说形参顺序,以本题为例
第3行代码
void solve (int zuoshangx, int zuoshangy, int youxiax, int youxiay, int gongzhux, int gongzhuy, int bianchang)
这个函数定义的形参传达了以下信息,即形参从左至右对应的是左上角点的x和y,右下角点的x和y,公主坐标的x和y,边长,这个信息的传递靠两个要素,一个是形参相对位置(从左到右),一个是形参变量名,相当于赋予了这个相对位置一个含义。
第14行代码
solve (zuoshangx, zuoshangy, middlex, middley, gongzhux, gongzhuy, bianchang/2);
对这段递归的理解就如上一段所讲,从左到右依次是左上角点的x和y,右下角点的x和y,公主坐标的x和y,边长,要在往这段函数填入传参之前意识到这一点,然后根据含义依次传入该含义对应的变量名②,即传入我下一次递归想要处理的正方形中的左上角点的x和y,右下角点的x和y,公主坐标的x和y,边长。
具体递归传参的方法(即②的进一步解释)
递归函数内部的递归时的传参是个难点(如14行代码),其实传参可以简单理解成传入变量名,而在递归函数内部可以用来的传参的变量名可以简单分成两大类(此处不考虑全局变量):定义递归函数时的形参名(如本题的zuoshangx,zuoshangy)和递归函数体内新定义的变量(如本题的middlex,middley)
例子
第14行代码
solve (zuoshangx, zuoshangy, middlex, middley, gongzhux, gongzhuy, bianchang/2);
这一行代码的目的是为了去处理中点左上角这一个矩形,为了实现这一目的我们要传入这一块矩形的左上角点的x和y,右下角点的x和y,公主坐标的x和y,边长,而这几个参数恰恰如上文所说由一开始定义递归函数的形参顺序所决定,所以我们只需要按照从左到右的顺序传入左上角点的x和y,右下角点的x和y,公主坐标的x和y,边长。再结合②所说,我们接着只需挑选合适的变量名,传入即可,对于这一行处理左上角矩形的代码,其左上角坐标为zuoshangx, zuoshangy,右下角坐标为middlex, middley,公主坐标为gongzhux, gongzhuy(其他三个递归的公主坐标就不是gongzhux和gongzhuy了而应该是图中对应的234格的坐标)。