刷题Day16递归与分治之铺地板问题

本文详细探讨了一道关于地毯填补问题的算法作业,该问题涉及使用四种特定形状的地毯来填充一个正方形区域,要求不重叠且除指定位置外完全覆盖。作者通过递归分治策略分析了解题思路,提出了一种按顺序铺满子区域的方法,并给出了AC代码实现。递归过程中考虑了子区域相对于原区域的偏移量,确保了输出顺序与样例一致。
摘要由CSDN通过智能技术生成

前言

 这道题是我在学算法分析与设计这门课的时候老师布置的一道作业题,再做的时候有点印象但是又不完全记得了。所以又重起炉灶写了一遍。
 先放题目来源:P1228 地毯填补问题
在这里插入图片描述
在这里插入图片描述
题意理解:
 首先,是题目会给出一个k值,这个k值会确定下一个 2 k ∗ 2 k 2^k*2^k 2k2k大小的正方形区块。现在题目给出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 22的子方形
                  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 2222的子方形再利用相同的"包裹"原则去连接剩余子方形,然后再利用相同方法就可以按要求铺满一个 2 3 ∗ 2 3 2^3*2^3 2323的方形。对于k=4,k=5…等也是一样的。
 到此为止,其实通过这样的一个方法找到了一个关键的做法,也是本题递归分治思想的来源。
解题思路:
 这样,对于一个 2 k ∗ 2 k 2^k*2^k 2k2k的正方形,我们可以将其拆分为四部分,左上,右上,左下和右下。然后我们可以利用 ( 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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值