分治算法:地毯填补问题

题目描述

相传在一个古老的阿拉伯国家里,有一座宫殿。宫殿里有个四四方方的格子迷宫,国王选择驸马的方法非常特殊,也非常简单:公主就站在其中一个方格子上,只要谁能用地毯将除公主站立的地方外的所有地方盖上,美丽漂亮聪慧的公主就是他的人了。公主这一个方格不能用地毯盖住,毯子的形状有所规定,只能有四种选择(如图):
在这里插入图片描述
并且每一方格只能用一层地毯,迷宫的大小为 2 k × 2 k 2^k\times 2^k 2k×2k 的方形。当然,也不能让公主无限制的在那儿等,对吧?由于你使用的是计算机,所以实现时间为 1 1 1s。

输入格式
输入文件共 2 2 2 行。
第一行: k k k,即给定被填补迷宫的大小为 2 k × 2 k 2^k\times 2^k 2k×2k ( 0 < k ≤ 10 (0\lt k\leq10 0<k10);
第二行: x , y x,y x,y,即给出公主所在方格的坐标( x x x 为行坐标, y y y 为列坐标), x x x y y y 之间有一个空格隔开。

输出格式
将迷宫填补完整的方案:每一补(行)为 x   y   c x\ y\ c x y c x , y x,y x,y为毯子拐角的行坐标和列坐标, c c c 为使用毯子的形状,具体见上面的图,毯子形状分别用 1 , 2 , 3 , 4 1,2,3,4 1,2,3,4 表示, x , y , c x,y,c x,y,c之间用一个空格隔开)。

算法思想(分治)

题目求的是在一个 2 k × 2 k 2^k\times 2^k 2k×2k的迷宫中,使用4种地毯覆盖除公主之外的所有位置,输出地毯放置的位置和地毯类型。

  • 先看最简单的情况,当 k = 1 k=1 k=1时,即迷宫大小为 2 × 2 2\times2 2×2,公主所在位置有下面4种情况,用一张地毯就可以覆盖其余位置。
    在这里插入图片描述
  • k = 2 k=2 k=2时,情况稍微复杂一些,但可以将它看作由 4 4 4 2 × 2 2\times2 2×2的迷宫组成的(如下图所示),那么公主必然在其中一个里,因此公主所在位置仍有4种情况。
    在这里插入图片描述
    这里我们假设公主在左上角的迷宫里,那么此时只需要在迷宫中间铺上一块地毯,将缺口面向公主所在迷宫(这里用的是1号地毯),这样就能保证每个小迷宫中都存在一个缺口,可以再使用4块地毯进行覆盖。
    在这里插入图片描述
  • k k k更大时,我们仍旧可以采用上述策略,将迷宫分成 4 4 4 2 k − 1 × 2 k − 1 2^{k-1}\times2^{k-1} 2k1×2k1的小迷宫,根据公主所在位置,在迷宫中间放置一块地毯,保证每个小迷宫都有一个缺口,然后使用相同的方法去处理4个小迷宫。

这种将一个复杂问题,分解成几个性质相同、规模更小的子问题,然后使用类似的思路去处理每个子问题的方法就是分治算法。

分治算法的基本思想是将一个规模为N的问题分解为K个规模较小的子问题,这些子问题相互独立且与原问题性质相同。求出子问题的解,就可得到原问题的解

算法实现

处理一个大小为 n × n n\times n n×n的迷宫,其左上角的坐标为 ( a , b ) (a,b) (a,b),此时公主所在为位置为 ( x , y ) (x,y) (x,y)

  • 如果 n = 1 n = 1 n=1,无需处理
  • 将迷宫分成 4 4 4个大小为 n 2 × n 2 \frac{n}{2}\times\frac{n}{2} 2n×2n的小迷宫
  • 判断公主所在位置
    • 选用对应地毯进行覆盖(该地毯的缺口指向公主所在的方格)
    • 递归处理每个小迷宫

代码实现

#include <iostream>
using namespace std;
//从左上角(a,b)开始处理大小为n×n的迷宫,(x,y)为公主或者已覆盖的位置
void dfs(int a, int b, int x, int y, int n)
{
    if(n == 1) return;
    n = n / 2;
    if(x <= a + n - 1 && y <= b + n - 1) //公主或者已覆盖的位置在左上角,使用1号地毯
    {    
        printf("%d %d 1\n", a + n, b + n);
        dfs(a, b, x, y, n); //公主位置不变
        dfs(a, b + n, a + n - 1, b + n, n);
        dfs(a + n, b, a + n, b + n - 1, n);
        dfs(a + n, b + n, a + n, b + n, n);
    }
    else if(x <= a + n - 1 && y >= b + n) //公主或者已覆盖的位置在右上角,使用2号地毯
    {
        printf("%d %d 2\n", a + n, b + n - 1);
        //分治,分成4个更小的部分递归处理
        dfs(a, b, a + n - 1, b + n - 1, n);
        dfs(a, b + n, x, y, n); //公主位置不变
        dfs(a + n, b, a + n, b + n - 1, n);
        dfs(a + n, b + n, a + n, b + n, n);
    }
    else if(x >= a + n && y <= b + n - 1) //公主或者已覆盖的位置在左下角,使用3号地毯
    {     
        printf("%d %d 3\n", a + n - 1, b + n);
        dfs(a, b, a + n - 1, b + n - 1, n);
        dfs(a, b + n, a + n - 1, b + n, n);
        dfs(a + n, b, x, y, n); //公主位置不变
        dfs(a + n, b + n, a + n, b + n, n);
    }
    else //公主或者已覆盖的位置在右下角,使用4号地毯
    {
        printf("%d %d 4\n", a + n - 1, b + n - 1);
        dfs(a, b, a + n - 1, b + n - 1, n);
        dfs(a, b + n, a + n - 1, b + n, n);
        dfs(a + n, b, a + n, b + n - 1, n);
        dfs(a + n, b + n, x, y, n); //公主位置不变
    }
}
int main()
{
    int x, y, k;
    scanf("%d%d%d", &k, &x, &y);
    int n = 1 << k; //迷宫大小
    dfs(1, 1, x, y, n);
    return 0;
}
  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

少儿编程乔老师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值