AcWing 98. 分形之城(递归+回溯)

题目链接:点击这里
在这里插入图片描述
在这里插入图片描述

这道题要求两个房子的直线距离,我们可以先求出两个房子的坐标,直接勾股定理来求。

首先要知道,一座 N N 级城市有 2 2 N 2^{2N} 个房子,我们先将问题缩小到 N 1 N-1 级城市, N 1 N-1 级城市的总房子数为 2 2 N 2 2^{2N-2} ,我们可以用 M   %   2 2 N 2 M\ \%\ 2^{2N-2} 来求得 N N 级城市编号为 M M 的房子 在 N 1 N-1 级城市中相应的编号,又可以根据 M   /   2 2 N 2 M\ /\ 2^{2N-2} 来确定 M M 位于 N 1 N-1 级城市四块中的哪一块。

整体思路是递归+回溯。

例如,对一个 N N 级城市编号为 M M 的房屋来说,不断利用 M   %   2 2 N 2 M\ \%\ 2^{2N-2} 得到该房屋在上一级城市中相应的编号,这样便可从 N N 级城市递归到 0 0 级城市,即递归边界是 0 0 级城市、其坐标为 ( 0 , 0 ) (0,0)
然后,不断利用 M   /   2 2 N 2 M\ /\ 2^{2N-2} 来确定房屋在当前级城市四块中的哪一块,再加上该块上正确的坐标变换,这样便可从 0 0 级城市回溯到 N N 级城市,从而得到 N N 级城市编号为 M M 的房屋的坐标。

剩下最麻烦的就是回溯过程中的坐标变换了:

为了取模计算方便,城市中房屋的编号及其位置坐标我们都从 0 0 开始:

第0块:从 1 1 级城市到 2 2 级城市左上角那一块:先绕着原点顺时针旋转90度,即 ( x ,   y ) (x,\ y) -> ( y , x ) (y,-x) ,然后水平翻转,即 ( y , x ) (y,-x) -> ( y , x ) (y,x) ,最终 ( x ,   y ) (x,\ y) -> ( y , x ) (y,x)

第1块:从 1 1 级城市到 2 2 级城市右上角那一块:这个比较简单,就是向右平移了 2 N 1 2^{N-1} ,即 ( x , y ) (x,y) -> ( x , y + 2 N 1 ) (x,y+2^{N-1})

第2块:从 1 1 级城市到 2 2 级城市右下角那一块:也比较简单,就是向右平移了 2 N 1 2^{N-1} ,然后向下平移了 2 N 1 2^{N-1} ,即 ( x , y ) (x,y) -> ( x + 2 N 1 y + 2 N 1 ) (x+2^{N-1},y+2^{N-1})

第3块:从 1 1 级城市到 2 2 级城市左下角那一块:这个较为麻烦,先绕着原点逆时针旋转90度,即 ( x ,   y ) (x,\ y) -> ( y ,   x ) (-y,\ x) ,再水平翻转,即 ( y ,   x ) (-y,\ x) -> ( y ,   x ) (-y,\ -x) ,然后向右平移 2 N 1 1 2^{N-1}-1 ,向下平移 2 2 N 1 1 2*2^{N-1}-1 ,即 ( y ,   x ) (-y,\ -x) -> ( 2 N y 1 ,   2 N 1 x 1 ) (2^{N}-y-1,\ 2^{N-1}-x-1) ,最终 ( x ,   y ) (x,\ y) -> ( 2 N y 1 ,   2 N 1 x 1 ) (2^{N}-y-1,\ 2^{N-1}-x-1)

对于上面的一系列坐标变换,能画图观察出来的最好,不能的话可以借助旋转矩阵来进行顺/逆时针旋转。

不管咋样,都要注意:本题的位置坐标指的是行号 x x 和列号 y y ,与数学中的坐标轴有所区别。

#include<cmath>
#include<cstdio>
#include<string>
#include<utility>
#include<cstring>
#include<iostream>
#include<algorithm> 
using namespace std;
typedef long long ll;

pair<ll,ll> calc(ll n, ll m)
{
    if(n==0)	return make_pair(0,0);			//递归边界,0级城 
    ll len = 1ll<<(n-1), cnt = 1ll<<(2*n-2);	//cnt是子问题里房屋的数量 
    pair<ll,ll> pos = calc(n - 1, m % cnt);
    ll x = pos.first, y = pos.second;
    ll z = m / cnt;
    if(z==0)	return make_pair(y, x);
    if(z==1)	return make_pair(x, y+len);
    if(z==2)	return make_pair(x+len, y+len);
    if(z==3)	return make_pair(2*len-y-1, len-x-1);
}

int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        ll N, A, B;
        scanf("%lld%lld%lld", &N, &A, &B);
        pair<ll,ll> apos = calc(N, A-1);
        pair<ll,ll> bpos = calc(N, B-1);
        double dx = apos.first - bpos.first, dy = apos.second - bpos.second;
        printf("%.0f\n", sqrt(dx*dx+dy*dy)*10);	//四舍五入到整数
    }
    return 0;
}
发布了727 篇原创文章 · 获赞 111 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/qq_42815188/article/details/104253470