每日一题 AcWing 98. 分形之城

本文探讨了名为Fractal的城市规划方案,涉及如何计算不同等级城市中特定街区A和B之间的直线距离。通过递归和分形结构,算法展示了计算街区坐标并确定距离的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

AcWing 98. 分形之城

题目:

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

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

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

city.png

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

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

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

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

输入格式

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

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

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

输出格式

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

数据范围

1 < = N < = 31 1<=N<=31 1<=N<=31

1 < = A , B < = 2 2 N 1<=A,B<=2^{2N} 1<=A,B<=22N

1 < = n < = 1000 1<=n<=1000 1<=n<=1000

输入样例:

3 
1 1 2 
2 16 1 
3 4 33 

输出样例:

10 
30 
50 

思路:

将编号为 M 的房屋(从0开始编号)在N级城市的坐标记为:calc(N,M)。

在求 calc(N,M)时,因为 N-1级城市有 2 2 N − 2 2^{2N-2} 22N2 座房子,我们递归求出 calc(N-1,M mod 2 2 N − 2 2^{2N-2} 22N2),

记求出的位置(x,y)从0开始编号,再根据 M/ 2 2 N − 2 2^{2N-2} 22N2 的大小算出处于哪一个位置。

1、如果在 左上角 则需要将 N-1 级的城市 顺时针旋转 90° 然后再水平旋转。

在这里插入图片描述

由图可知:坐标 由(x,y)变成 ( y , 2 N − 1 − x − 1 y,2^{N-1}-x-1 y2N1x1) 再变成 (y, x)

2、如果在 右上角 则坐标由 (x,y) 变成 ( x , y + 2 N − 1 x,y+2^{N-1} x,y+2N1

3、如果在 右下角 则坐标由 (x,y) 变成 ( x + 2 N − 1 , y + 2 N − 1 x+2^{N-1},y+2^{N-1} x+2N1,y+2N1

4、如果在 左下角 则需要将 N - 1 级城市逆时针旋转90°,再水平旋转

坐标由 (x,y) 变成 ( 2 N − 1 − y − 1 , 2 N − 1 − x − 1 2^{N-1} - y - 1, 2^{N-1}-x-1 2N1y1,2N1x1),根据位置还需要将行号加上 2 N − 1 2^{N-1} 2N1,

坐标变成( 2 N − y − 1 , 2 N − 1 − x − 1 2^N - y - 1, 2^{N-1}-x-1 2Ny1,2N1x1)

代码:

package 基本算法.递推与递归.分形之城;

import java.util.Scanner;

public class Main {
	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		int t = scanner.nextInt();
		while(t-- > 0) {
			int n = scanner.nextInt();
			long A = scanner.nextLong();
			long B = scanner.nextLong();
			Point ac = calc(n, A - 1);
			Point bc = calc(n, B - 1);
			double x = ac.getX() - bc.getX(), y = ac.getY() - bc.getY();
			System.out.println(Math.round(Math.sqrt(x*x + y*y) * 10));
		}
		scanner.close();
	}
	
	public static Point calc(int n, long m) {
		if(n == 0) return new Point(0, 0);
		long len = (long)1 << (n - 1), cnt = (long)1 << (2 * n - 2);
		Point pos = calc(n - 1, m % cnt);
		long x = pos.getX(), y = pos.getY();
		long z = m / cnt;
		if(z == 0) return new Point(y, x);
		if(z == 1) return new Point(x, y + len);
		if(z == 2) return new Point(x + len, y + len);
		if(z == 3) return new Point(2 * len - y - 1, len - x - 1);
		return new Point(-1, -1);
	}
}

class Point{
	private long x;
	private long y;
	public Point(long x, long y) {
		this.x = x;
		this.y = y;
	}
	public long getX() {
		return x;
	}
	public long getY() {
		return y;
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值