分形:分形之城 / POJ3889 Fractal Streets

题目地址 AcWing98分形之城

题目描述

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

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

而这座名为 Fractal 的城市设想了这样的一个规划方案,如下图所示:

city.png

当城区规模扩大之后,Fractal 的解决方案是把和原来城区结构一样的区域按照图中的方式建设在城市周围,提升城市的等级。

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

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

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

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

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

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

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

数据范围
1≤N≤31,
1≤A,B≤22N,
1≤n≤1000

样例

输入样例:

3 
1 1 2 
2 16 1 
3 4 33 

输出样例:

10 
30 
50 

算法分析&C++11代码

1. 以每个图形的中心点作为原点;
2. 当前坐标在图形中的位置可以由序号所在小图形(左上/右上/左下/右下)和该点在对应小图形中的位置决定!
左上角和左下角的小图形需要翻转,而右上角的和右下角的不变,之后将小图形的坐标原点变换到当前大图形的坐标原点上;
  • 左上角的需要进行顺时针 90° 旋转后沿 Y 轴对称,(x,y)—>(y,-x) —>(-y,-x)
  • 左下角的需要进行逆时针 90° 旋转后沿 Y 轴对称,(x,y)—>(-y,x)—>(y,x)
  • 右上角和右下角的保持不变,(x,y)
  • 进行坐标原点变换,为避免出现小数,采用 len = 1 << ( level - 1 ) ,level为当前图形级别;此时变换的单位长度应是 len / 2 才对,但是为此处避免出现小数,抽出边长 10 的 2 作为因子,之后计算距离后只需要乘以 5 即可。
    如左上角(-y,-x)—>(-y - len,-x + len),右上/左下/右下坐标原点变换后依次为 (x + len,y + len)(y - len,x - len)(x + len,y - len)
3.递归结束于 level == 0 ,此时 x = 0,y = 0
4.计算两点距离,结果乘以( 10 / 2 ,前面已经为不出现小数乘过一个 2 )
#include <cmath>
#include <iostream>
// #define debug
using namespace std;
using Long = long long int;
using Longpair = pair<Long, Long>;
Longpair findindex(int level, Long dis)
{
    if (!level)
        return {0LL, 0LL};
    auto len{1 << (level - 1)};
    auto space{1LL << (2 * level - 2)};
    auto diss{findindex(level - 1, dis % space)};
    auto x{diss.first}, y{diss.second};
    switch (dis / space)
    {
    case 0LL:
        return {-y - len, -x + len};
    case 1LL:
        return {x + len, y + len};
    case 2LL:
        return {x + len, y - len};
    case 3LL:
        return {y - len, x - len};
    }
}
int main(int argc, char **argv)
{
    int cnt;
    cin >> cnt;
    while (cnt--)
    {
        int level;
        Long A, B;
        cin >> level >> A >> B;
        auto tempA{findindex(level, A - 1)}, tempB{findindex(level, B - 1)};
#ifdef debug
        cout << "----------" << endl;
        cout << tempA.first << ',' << tempA.second << endl;
        cout << tempB.first << ',' << tempB.second << endl;
        cout << "----------" << endl;
#endif
        double x = tempA.first - tempB.first;
        double y = tempA.second - tempB.second;
        cout.flags(ios::fixed);
        cout.precision(0);
        cout << sqrt(x * x + y * y) * 5.0 << endl;
    }
    return EXIT_SUCCESS;
}

END

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值