应用问题的分治策略求解-铺地砖

文章讨论了如何在一个由2^n×2^n网格组成的游泳池底部铺砖,要求避开一个排水口。通过分治递归策略,设计了普通铺砖和中心铺砖函数,以及中心划分函数,以确保排水口不被覆盖且其他区域恰好被一块砖覆盖。算法的时间复杂度为O(n^2logn),空间复杂度为O(n^2)。
摘要由CSDN通过智能技术生成

算法设计与分析_铺地砖

问题

有一个游泳池底,划分成了 2 n × 2 n 2^n \times 2^n 2n×2n(其中 n n n是正整数)的网格,每一小格都是正方形,其中有一小格是排水口。

下图展示了当 n = 4 n=4 n=4时,在坐标 ( 11 , 11 ) (11,11) (11,11)上有一个排水口的情景。

piTZiRJ.png

现允许用4种形状(如果考虑旋转和反转,实质上是同一种)的地砖进行铺设,要求排水口不能被覆盖,其他小格恰被一块地砖覆盖。

piTZVqx.png

请问任务能否完成,为什么。

如果能完成,请设计算法实现上述要求的覆盖。

问题分析与论证过程

(如果能完成,请给出严格的证明。如果不能完成,请举反例。)

问题分析

铺砖问题分治递归的算法可以通过以下步骤进行分析:
1.定义问题:铺砖问题是一个将给定的游泳池区域铺满砖块的问题。游泳池区域被分成若干个2x2的方格,每3个方格可以选择铺一块砖。其中有一块排水口,不用铺砖,目标是找到一种铺砖方式,使得整个游泳池区域被完全铺满。

2.设计算法:具体铺砖的方式可以分为普通铺砖和中心铺砖两种,普通铺砖即2x2方格中有一个1,其余为0 (用1选择方式去铺剩余3个0),中心铺砖2x2方格中有一个2其他为1 (用2选择方式取铺剩余3个1)即分别用函数puzh()和zhxin()表示。设计函数zxhf()来划分中心区域,确定实际排水口位置,并将该区域分成四个相同的子区域,并在这四个相同的区域分别设置一个伪排水口。使用递归的方式,将铺砖问题不断分解成子问题,直到最小范围的子问题可以直接处理。铺砖过程以遍历每一块砖为方法,若遍历到为1的方格,采用普通铺砖去铺2x2方格中剩余的3个0;遍历到为2的方格,采用中心铺砖,去铺2x2剩余的3个1。注意特殊情况,即真排水口在某一层中心点上的情况,原本为1,因为是中心四块方格也是距离排水口最近的中心方格成为了2 。这时除了要采用中心铺砖,去铺一个2x2区域剩余的3个1,还要采用普通铺砖用2去铺另一个区域剩下的3个0;

3.分析复杂度:铺砖问题的复杂度主要取决于游泳池区域的大小,假设游泳池区域的边长为n。
在划分中心区域时,需要计算每个中心方格与排水口的距离,这个计算时间复杂度为O(1)。
递归的过程中,问题被分解为四个子问题,每个子问题的规模为原问题的四分之一,因此递归的时间复杂度为O(log n)。
综上所述,铺砖问题分治递归算法的总体时间复杂度为O(n^2 log n),空间复杂度为O(n^2)(用来存储游泳池区域的二维数组)。

论证过程

接下来以8x8的方格为例。证明其实现过程:

以下是一个8x8个方格的游泳池,其中设(2,6)为排水口。

1.初始状态,剩余63个方格为0,(2,6)方格为1。

piAO13Q.jpg

2.找到8x8方格的四个中心方格,离真排水口最近的设置为2,其余三个方格设置为1,即当作递归后四个区域的伪排水口。

piAOUEV.jpg

3.以8/2=4为方格宽度,分治划分为四个4x4方格的小区域,并以四个区域内的数字1为排水口执行在四个4x4的方格中执行与第二步相同的操作。注意右上角部分的橙色二,此时出现了问题分析中出现的特殊情况:即真排水口在某一层中心点上的情况,原本为1,因为其也是中心四块方格,也是距离排水口最近的中心方格成为了2 。

piAOaNT.jpg

4.重复执行2、3步直至已经分治为了全部为2x2的方格。(本例此时已经成为了2x2的方格)

5.从第一块方格开始遍历 ,若遍历到值为1的方格,通过判断值为它的方格在2x2的区域的位置,去为2x2区域中剩下的3个方格挑选合适的砖型铺砖,即普通铺砖;若遍历值为2的方格,通过判断值为它的方格在2x2的区域的位置,去为2x2区域中剩下的3个方格(每层中心位置的四个方格)挑选合适的砖型铺砖,即中心铺砖;面对上述出现的特殊情况,如本例上图所示,当遍历到橙色2时,其既要执行中心铺砖去铺其所对左下角的三个方格,也要执行普通铺砖,通过判断值为它的方格在2x2的区域的位置,去为左上角剩下的三个方格挑选合适的砖型铺砖。铺砖完成后效果图如下:

piAOd4U.jpg

6.如上图所示,对于该问题的算法分析,是可以实现本题要求的。

算法设计

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
int num[1027][1027]={0};//二维数组用来表示游泳池方格 
void puzh(int x,int y);//普通铺砖:2*2方格中有一个1,其余为0 (用1铺0) 
void zhxin(int x,int y);//中心铺砖:2*2方格中有一个2其他为1 (用2铺1) 
void zxhf(int num[1027][1027],int zx,int zy,int x,int y,int width);//中心划分 
void huafen(int num[1027][1027],int ax,int ay,int x,int y,int width);//总划分 
void puzh(int x,int y)//普通铺砖:2*2方格中有一个1,其余为0 (用1铺0)
   {  if (x % 2 == 0 && y % 2 == 0)//1在2*2的右下角其他为0选方式一铺砖 
	    {
	        printf("1 %d %d %d %d %d %d\n", x - 1, y - 1, x - 1, y, x, y - 1);
	    }
	    if (x % 2 == 0 && y % 2 != 0)//1在2*2的左下角其他为0选方式二铺砖  
	    {
	        printf("2 %d %d %d %d %d %d\n", x - 1, y + 1, x - 1, y, x, y + 1);
	    }
	    if (x % 2 != 0 && y % 2 != 0)//1在2*2的左上角其他为0选方式三铺砖 
	    {
	        printf("3 %d %d %d %d %d %d\n", x + 1, y + 1, x + 1, y, x, y + 1);
	    }
	    if (x % 2 != 0 && y % 2 == 0)//1在2*2的右上角其他为0选方式四铺砖  
	    {
	        printf("4 %d %d %d %d %d %d\n", x + 1, y - 1, x + 1, y, x, y - 1);
	    }
}
void zhxin(int x, int y)//中心铺砖:2*2方格中有一个2其他为1 (用2铺1)
{
    if (x % 2 != 0 && y % 2 != 0)//2在每层中心2*2的右下角其他为1选方式一铺砖 
    {
        printf("1 %d %d %d %d %d %d\n", x - 1, y - 1, x - 1, y, x, y - 1);
    }
    if (x % 2 != 0 && y % 2 == 0)//2在每层中心2*2的左下角其他为1选方式二铺砖 
    {
        printf("2 %d %d %d %d %d %d\n", x - 1, y + 1, x - 1, y, x, y + 1);
    }
    if (x % 2 == 0 && y % 2 == 0)//2在每层中心2*2的左上角其他为1选方式三铺砖 
    {
        printf("3 %d %d %d %d %d %d\n", x + 1, y + 1, x + 1, y, x, y + 1);
    }
    if (x % 2 == 0 && y % 2 != 0)//2在每层中心2*2的右上角其他为1选方式四铺砖 
    {
        printf("4 %d %d %d %d %d %d\n", x + 1, y - 1, x + 1, y, x, y - 1);
    }
}
 
void zxhf(int num[1027][1027],int zx,int zy,int x,int y,int width)// 中心划分 
{    float dis1,dis2,dis3,dis4;
//以下计算中心四块方格与排水口的距离 
      dis1=sqrt(pow(zx-x,2)+pow(zy-y,2)); //中心左上角与排水口距离 
      dis2=sqrt(pow(zx-x,2)+pow((zy+1)-y,2));//中心右上角与排水口距离 
      dis3=sqrt(pow((zx+1)-x,2)+pow(zy-y,2));//中心左下角与排水口距离  
      dis4=sqrt(pow((zx+1)-x,2)+pow((zy+1)-y,2));//中心右下角与排水口距离  
//以下为若中心左上角方格距离排水口最近,中心左上角方格设置为2,表示为排水口在这一部分
//其他中心三块方格设置为1,作为伪排水口,以便分治的四部分面对的问题相同 
//然后分支递归huafen函数,将原来的大区域划分为四个相同的小区域 
	 if((dis1<dis2)&&(dis1<dis3)&&(dis1<dis4))
	 {
	 	num[zx][zy]=2;//中心左上角设置为2 
	 	num[zx][zy+1]=1;//中心右上角设置为1 
	 	num[zx+1][zy]=1;//中心左下角设置为1 
	 	num[zx+1][zy+1]=1;//中心右下角设置为1 
	 	width=width/2;//宽度除2 
		huafen(num,zx,zy,x,y,width);//以(x,y)为排水口,递归左上部分,(zx,zy)为其右下角方格坐标 
		huafen(num,zx,zy+width,zx,zy+1,width);//以(zx,zy+1)为排水口,递归右上部分,(zx,zy+width)为其右下角方格坐标
		huafen(num,zx+width,zy,zx+1,zy,width);//以(zx+1,zy)为排水口,递归左下部分,(zx+width,zy)为其右下角方格坐标
		huafen(num,zx+width,zy+width,zx+1,zy+1,width);//以(zx+1,zy+1)为排水口,递归右下部分,(zx+width,zy+width)为其右下角方格坐标
	 }
//以下为若中心右上角方格距离排水口最近,中心右上角方格设置为2,表示为排水口在这一部分
//其他中心三块方格设置为1,作为伪排水口,以便分治的四部分面对的问题相同 
//然后分支递归huafen函数,将原来的大区域划分为四个相同的小区域 
	if((dis2<dis1)&&(dis2<dis3)&&(dis2<dis4))
	 {
	 	num[zx][zy]=1;//中心左上角设置为1 
	 	num[zx][zy+1]=2;//中心右上角设置为2 
	 	num[zx+1][zy]=1;//中心左下角设置为1 
	 	num[zx+1][zy+1]=1;//中心右下角设置为1 
	 	width=width/2;//宽度除2
		huafen(num,zx,zy,zx,zy,width);//以(zx,zy)为排水口,递归左上部分,(zx,zy)为其右下角方格坐标  
		huafen(num,zx,zy+width,x,y,width);//以(x,y)为排水口,递归右上部分,(zx,zy+width)为其右下角方格坐标 
		huafen(num,zx+width,zy,zx+1,zy,width);//以(zx+1,zy)为排水口,递归左下部分,(zx+width,zy)为其右下角方格坐标 
		huafen(num,zx+width,zy+width,zx+1,zy+1,width);//以(zx+1,zy+1)为排水口,递归右下部分,(zx+width,zy+width)为其右下角方格坐标 
	 }
//以下为若中心左下角方格距离排水口最近,中心左下角方格设置为2,表示为排水口在这一部分
//其他中心三块方格设置为1,作为伪排水口,以便分治的四部分面对的问题相同 
//然后分支递归huafen函数,将原来的大区域划分为四个相同的小区域 
	if((dis3<dis1)&&(dis3<dis2)&&(dis3<dis4))
	 {
	 	num[zx][zy]=1;//中心左上角设置为1 
	 	num[zx][zy+1]=1;//中心右上角设置为1 
	 	num[zx+1][zy]=2;//中心左下角设置为2 
	 	num[zx+1][zy+1]=1;//中心右下角设置为1 
		width=width/2;//宽度除2
		huafen(num,zx,zy,zx,zy,width);//以(zx,zy)为排水口,递归左上部分,(zx,zy)为其右下角方格坐标 
		huafen(num,zx,zy+width,zx,zy+1,width);//以(zx,zy+1)为排水口,递归右上部分,(zx,zy+width)为其右下角方格坐标 
		huafen(num,zx+width,zy,x,y,width);//以(x,y)为排水口,递归左下部分,(zx+width,zy)为其右下角方格坐标 
		huafen(num,zx+width,zy+width,zx+1,zy+1,width);//以(zx+1,zy+1)为排水口,递归右下部分,(zx+width,zy+width)为其右下角方格坐标 	
	 }
//以下为若中心右下角方格距离排水口最近,中心右下角方格设置为2,表示为排水口在这一部分
//其他中心三块方格设置为1,作为伪排水口,以便分治的四部分面对的问题相同 
//然后分支递归huafen函数,将原来的大区域划分为四个相同的小区域 
	if((dis4<dis1)&&(dis4<dis2)&&(dis4<dis3))
	 {
	 	num[zx][zy]=1;//中心左上角设置为1
	 	num[zx][zy+1]=1;//中心右上角设置为1
	 	num[zx+1][zy]=1;//中心左下角设置为1
	 	num[zx+1][zy+1]=2;//中心右下角设置为2 
		width=width/2;//宽度除2
		huafen(num,zx,zy,zx,zy,width);//以(zx,zy)为排水口,递归左上部分,(zx,zy)为其右下角方格坐标 
		huafen(num,zx,zy+width,zx,zy+1,width);//以(zx,zy+1)为排水口,递归右上部分,(zx,zy+width)为其右下角方格坐标 
		huafen(num,zx+width,zy,zx+1,zy,width);//以(zx+1,zy)为排水口,递归左下部分,(zx+width,zy)为其右下角方格坐标 
		huafen(num,zx+width,zy+width,x,y,width);//以(x,y)为排水口,递归右下部分,(zx+width,zy+width)为其右下角方格坐标 
	 }
}
void huafen(int num[1027][1027],int ax,int ay,int x,int y,int width)//总划分
{
	if(width/2==1)//当划分为2*2的问题时停止,因为2*2的格子已经只能容纳一块砖 
	 return;
	 else
	 {
	   int 	zx=(2*ax-width)/2;//中心点横坐标 
        int zy=(2*ay-width)/2;//中心点纵坐标 
	 	zxhf(num,zx,zy,x,y,width);//通过中心划分,从中心开始将原来的大区域划分为等同的四个小区域		 	
	 }
}
int main()
{
	int n,x,y;//n:有2的n次方*2的n次方个方格,(x,y):真排水口坐标 
	scanf("%d%d%d",&n,&x,&y);
	int gnum=pow(2,n);//gnum:每行或每列的方格数 
	for(int i=1;i<=gnum;i++)
    for(int j=1;j<=gnum;j++)
     num[i][j]=0;//需要铺地砖的方格二维数组中初始为0,表示未铺砖。 
	num[x][y]=1;//排水口初始化为1,表示不需要铺砖    
	int  ax=gnum,ay=gnum;//最右下角地砖坐标 
	int width=gnum;//当前宽度(方格每行多少个) 
	huafen(num,ax,ay,x,y,width); //进入划分函数,分治递归将方格最终划分为2*2的小问题 
//以下为铺砖过程 
    for(int i=1;i<=gnum;i++) 
    {for(int j=1;j<=gnum;j++)
       {   if(num[i][j]==1)//2*2方格中有一个1,其余为0 (用1铺0) 
             { 
			   puzh(i,j);}            
            if(num[i][j]==2)//2*2方格中有一个2其他为1 (用2铺1)
             {   zhxin(i,j);
                if(i==x&&j==y)//真排水口在某一层中心点上的情况,原本为1,因为其也是距离排水口最近的中心方格成为了2  
				 puzh(i,j);                
            }
        }
    }
    return 0; 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值