【算法刷题】AcWing 98. 分形之城——递归

城市的规划在城市建设中是个大问题。

不幸的是,很多城市在开始建设的时候并没有很好的规划,城市规模扩大之后规划不合理的问题就开始显现。

而这座名为 F r a c t a l Fractal Fractal 的城市设想了这样的一个规划方案,如下图所示:
【算法刷题】AcWing 98. 分形之城——递归_第1张图片
当城区规模扩大之后,Fractal 的解决方案是把和原来城区结构一样的区域按照图中的方式建设在城市周围,提升城市的等级。

对于任意等级的城市,我们把正方形街区从左上角开始按照道路标号。

虽然这个方案很烂,Fractal 规划部门的人员还是想知道,如果城市发展到了等级 N N N,编号为 A A A B B B 的两个街区的直线距离是多少。

街区的距离指的是街区的中心点之间的距离,每个街区都是边长为 10 10 10 米的正方形。

输入格式
第一行输入正整数 n n n,表示测试数据的数目。

以下 n n n 行,输入 n n n 组测试数据,每组一行。

每组数据包括三个整数 N , A , B N,A,B N,A,B,表示城市等级以及两个街区的编号,整数之间用空格隔开。

输出格式
一共输出 n n n 行数据,每行对应一组测试数据的输出结果,结果四舍五入到整数。

数据范围
1 ≤ N ≤ 31 1 \leq N \leq 31 1N31
1 ≤ A , B ≤ 2 2 N 1 \leq A,B \leq 2^{2N} 1A,B22N
1 ≤ n ≤ 1000 1 \leq n \leq 1000 1n1000
输入样例

3 
1 1 2 
2 16 1 
3 4 33 

输出样例

10 
30 
50 
①分析

找规律发现下一级由四个上一级组成,从左到右从上到下依次为: 关于 y = x 轴对称  ∣  不变 关于 . . . 轴对称  ∣  不变 在 N ≥ 1 下看 A 、 B 落在四块中的哪一个部分,然后进行递归 整个图形的边长为 2 N ,则每一部分的边长为 2 N − 1 ,则每一部分有 2 2 ( N − 1 ) 个数字 看 A 属于哪一块: A / 2 2 N − 2 余数为 0 在左上角,为 1 在右上角,为 2 在右下角,为 3 在左下角 设 g e t ( N , A ) 为获取 A 的坐标函数,该函数则经过上面推导可以递推到 g e t ( N − 1 , A % 2 2 N − 2 ) 如果递推后求得坐标为 ( x , y ) ,但这个是在递推之后的 N − 1 级坐标系求得的,需要将其转化为 N 级的坐标系坐标 余数为 0 : ( y , x ) 余数为 1 : ( x , y + 2 N − 1 ) 余数为 2 : ( x + 2 N − 1 , y + 2 N − 1 ) 余数为 3 : ( 2 N − 1 + 2 N − 1 − 1 − y , 2 N − 1 − 1 − x ) 找规律发现下一级由四个上一级组成,从左到右从上到下依次为:\\ 关于y=x轴对称\ |\ 不变\\ 关于...轴对称\ |\ 不变\\ 在N\geq1下看A、B落在四块中的哪一个部分,然后进行递归\\ 整个图形的边长为2^N,则每一部分的边长为2^{N-1},则每一部分有2^{2(N-1)}个数字\\ 看A属于哪一块:A/2^{2N-2}余数为0在左上角,为1在右上角,为2在右下角,为3在左下角\\ 设get(N,A)为获取A的坐标函数,该函数则经过上面推导可以递推到get(N-1,A\%2^{2N-2})\\ 如果递推后求得坐标为(x,y),但这个是在递推之后的N-1级坐标系求得的,需要将其转化为N级的坐标系坐标\\ 余数为0:(y,x)\\ 余数为1:(x,y+2^{N-1})\\ 余数为2:(x+2^{N-1},y+2^{N-1})\\ 余数为3:(2^{N-1}+2^{N-1}-1-y,2^{N-1}-1-x)\\ 找规律发现下一级由四个上一级组成,从左到右从上到下依次为:关于y=x轴对称  不变关于...轴对称  不变N1下看AB落在四块中的哪一个部分,然后进行递归整个图形的边长为2N,则每一部分的边长为2N1,则每一部分有22(N1)个数字A属于哪一块:A/22N2余数为0在左上角,为1在右上角,为2在右下角,为3在左下角get(N,A)为获取A的坐标函数,该函数则经过上面推导可以递推到get(N1,A%22N2)如果递推后求得坐标为(x,y),但这个是在递推之后的N1级坐标系求得的,需要将其转化为N级的坐标系坐标余数为0(y,x)余数为1(x,y+2N1)余数为2(x+2N1,y+2N1)余数为3(2N1+2N11y,2N11x)

②代码
#include 
#include 

typedef long long LL;

struct Point
{
    LL x, y;
};

Point get(LL n, LL a)
{
    if (n == 0)
        return {0, 0};
    LL block = 1ll << n * 2 - 2, len = 1ll << n - 1;
    auto p = get(n - 1, a % block);
    LL x = p.x, y = p.y;
    int z = a / block;
    
    if (z == 0)
        return {y, x};
    else if (z == 1)
        return {x, y + len};
    else if (z == 2)
        return {x + len, y + len};
    else
        return {len * 2 - 1 - y, len - 1 - x};
}

int main()
{
  int t;
  scanf("%d", &t);
  while (t--)
  {
      LL n, a, b;
      scanf("%lld %lld %lld", &n, &a, &b);
      auto pa = get(n, a - 1);
      auto pb = get(n, b - 1);
      double dx = pa.x - pb.x, dy = pa.y - pb.y;
      printf("%.0lf\n", sqrt(dx *dx + dy * dy) * 10);
  }
  
  return 0;
}
③细节分析
  • 为了方便操作,不一定要按着题目给的编号来给数组,可以让下标统一减1

你可能感兴趣的:(Algorithm,算法)