前言
这道题是我在学算法分析与设计这门课的时候老师布置的一道作业题,再做的时候有点印象但是又不完全记得了。所以又重起炉灶写了一遍。
先放题目来源:P1228 地毯填补问题
题意理解:
首先,是题目会给出一个k值,这个k值会确定下一个
2
k
∗
2
k
2^k*2^k
2k∗2k大小的正方形区块。现在题目给出4种地毯形状,即(方便后面用计算机表示了):
第一种方毯
⇒
\Rightarrow
⇒
0
1
1
1
\begin{matrix}0&1\\1&1 \end{matrix}
0111 第二种方毯
⇒
\Rightarrow
⇒
2
0
2
2
\begin{matrix}2&0\\2&2 \end{matrix}
2202
第三种方毯
⇒
\Rightarrow
⇒
3
3
0
3
\begin{matrix}3&3\\0&3 \end{matrix}
3033 第四种方毯
⇒
\Rightarrow
⇒
4
4
4
0
\begin{matrix} 4&4\\4&0 \end{matrix}
4440
用这四种方毯去往给定大小的方形区块铺,并且满足不重叠和除给定位置
(
x
,
y
)
(x,y)
(x,y)外均需铺满。现在题目要求我们给出铺满这个方形区域的过程。
在这里想说一声,抛开样例输出不谈,其实输出的顺序可以有很多种而不仅仅局限于样例输出顺序,但是为了追求逻辑上的完美,所以原题目作者安排了这样的输出顺序(猜的)。
基于题意的思考:
首先,假如公主位置
(
x
,
y
)
(x,y)
(x,y)为
(
1
,
1
)
(1,1)
(1,1),这样的话,第一种方毯恰好满足条件。这就是说:
L
1
1
1
\begin{matrix} L&1\\1&1 \end{matrix}
L111
对于其余三个位置,也可以得到相同的结论。即当k=1的时候,很容易知道可以铺成功。
当k=2的时候,实际上这个大方形是四个
2
∗
2
2*2
2∗2的子方形
0
0
0
0
\begin{matrix} 0&0\\0&0 \end{matrix}
0000
0
0
0
0
\begin{matrix} 0&0\\0&0 \end{matrix}
0000
0
0
0
0
\begin{matrix} 0&0\\0&0 \end{matrix}
0000
0
0
0
0
\begin{matrix} 0&0\\0&0 \end{matrix}
0000
这时候,不论公主的位置出现在哪个子方形里,那个子方形都可以被铺满。假如公主位置为
(
1
,
1
)
(1,1)
(1,1),即:
L
0
0
0
\begin{matrix}L&0\\0&0 \end{matrix}
L000
0
0
0
0
\begin{matrix} 0&0\\0&0 \end{matrix}
0000
0
0
0
0
\begin{matrix} 0&0\\0&0 \end{matrix}
0000
0
0
0
0
\begin{matrix} 0&0\\0&0 \end{matrix}
0000
那么可以利用第一种地毯铺第一个子方形,即:
L
1
1
1
\begin{matrix}L&1\\1&1 \end{matrix}
L111
0
0
0
0
\begin{matrix} 0&0\\0&0 \end{matrix}
0000
0
0
0
0
\begin{matrix} 0&0\\0&0 \end{matrix}
0000
0
0
0
0
\begin{matrix} 0&0\\0&0 \end{matrix}
0000
也就是说,不论公主出现在哪个子方形里,这个子方形都会被铺满;又已知子方形中任意一个格子被占,又可以找到一种地毯去铺满这个子方形。因此,若利用一个地毯去包裹住已经被铺好的子方形,即:
L
1
1
1
\begin{matrix}L&1\\1&1 \end{matrix}
L111
0
0
1
0
\begin{matrix} 0&0\\1&0 \end{matrix}
0100
0
1
0
0
\begin{matrix} 0&1\\0&0 \end{matrix}
0010
1
0
0
0
\begin{matrix} 1&0\\0&0 \end{matrix}
1000
这样,剩余的子方形也可以被铺满。
$emsp;依次递推,当k=3时,先利用上述方法铺满一个
2
2
∗
2
2
2^2*2^2
22∗22的子方形再利用相同的"包裹"原则去连接剩余子方形,然后再利用相同方法就可以按要求铺满一个
2
3
∗
2
3
2^3*2^3
23∗23的方形。对于k=4,k=5…等也是一样的。
到此为止,其实通过这样的一个方法找到了一个关键的做法,也是本题递归分治思想的来源。
解题思路:
这样,对于一个
2
k
∗
2
k
2^k*2^k
2k∗2k的正方形,我们可以将其拆分为四部分,左上,右上,左下和右下。然后我们可以利用
(
x
,
y
)
(x,y)
(x,y)确定公主的位置在哪一个子方形里,然后利用之前"包裹"的做法,先假设公主所在的子方形已经铺好,然后选择一个合适的地板包裹好这个被铺好的子方形,然后在剩余子方形里把铺地板占用的位置当做公主位置来进行相同操作即可。注意到题目样例输出顺序,因此,合适的递归遍历顺序是左上,右上,左下和右下。这里还需要注意一个地方,也就是递归下去的时候,为了确定位置,还需要另外一对参数,即当前递归到的子方形相对于原方形的偏移量。这个很容易理解,我不再赘述了。最后给出我的AC代码:
#include <stdio.h>
#include <stdlib.h>
int my_pow(int base,int k){
int i,sum=1;
for(i=1;i<=k;i++)
sum*=base;
return sum;
}
void find(int x,int y,int part_x,int part_y,int length){
if(length==1)
return;
if(x<=part_x+length/2&&y<=part_y+length/2){
printf("%d %d %d\n",part_x+length/2+1,part_y+length/2+1,1);
find(x,y,part_x,part_y,length/2);
find(part_x+length/2,part_y+length/2+1,part_x,part_y+length/2,length/2);
find(part_x+length/2+1,part_y+length/2,part_x+length/2,part_y,length/2);
find(part_x+length/2+1,part_y+length/2+1,part_x+length/2,part_y+length/2,length/2);
}else if(x<=part_x+length/2&&y>=part_y+length/2+1){
printf("%d %d %d\n",part_x+length/2+1,part_y+length/2,2);
find(part_x+length/2,part_y+length/2,part_x,part_y,length/2);
find(x,y,part_x,part_y+length/2,length/2);
find(part_x+length/2+1,part_y+length/2,part_x+length/2,part_y,length/2);
find(part_x+length/2+1,part_y+length/2+1,part_x+length/2,part_y+length/2,length/2);
}else if(x>=part_x+length/2+1&&y<=part_y+length/2){
printf("%d %d %d\n",part_x+length/2,part_y+length/2+1,3);
find(part_x+length/2,part_y+length/2,part_x,part_y,length/2);
find(part_x+length/2,part_y+length/2+1,part_x,part_y+length/2,length/2);
find(x,y,part_x+length/2,part_y,length/2);
find(part_x+length/2+1,part_y+length/2+1,part_x+length/2,part_y+length/2,length/2);
}else if(x>=part_x+length/2+1&&y>=part_y+length/2+1){
printf("%d %d %d\n",part_x+length/2,part_y+length/2,4);
find(part_x+length/2,part_y+length/2,part_x,part_y,length/2);
find(part_x+length/2,part_y+length/2+1,part_x,part_y+length/2,length/2);
find(part_x+length/2+1,part_y+length/2,part_x+length/2,part_y,length/2);
find(x,y,part_x+length/2,part_y+length/2,length/2);
}
}
int main(int argc, char *argv[]) {
int x,y,k,length;
scanf("%d",&k);
length=my_pow(2,k);
scanf("%d%d",&x,&y);
find(x,y,0,0,length); //已经占了(x,y)的位置,偏移量为(0,0),此时正方形宽为length,找铺盖方法
return 0;
}