题目描述
小明有一个爱好,他喜欢在一个无限大的平面上散步,并且他散步的路径是有规律的。每次他会从(1,1)点出发,经过2n x
2n个点,最后在(2n,1)处停下。 他第1、2、3天的散步路径如下图所示。
当n>1时,他第n天的散步路径可以通过如下方法构造:
1 将第n-1阶的路径顺时针旋转90度,之后放在左下角;
2 将第n-1阶的路径放在左上角;
3 将图形沿着中轴线对称构造右半部份;
4 用3条单位线段把四部分连接起来。
现在,小明想要知道,他在第n天走过的某个格子是他 当天走过的第几个格子?
输入数据
一行三个整数n,x,y(1<=n<=14,1<=x,y<=2n) 。
输出数据
一行一个整数k,表示坐标为(x,y)的格子是他第n天走过的第k个格子。
样例输入
1 1 2
样例输出
2
解题思路
- 首先明确,天数n也就是每一轮拓扑的迭代次数。每一次迭代参考的最基本模型就是第一天的路径。
- 我们整个路径的拓扑是分四模块来考虑的,左下-左上-右上-右下。
- 确定当前是在哪一模块。
- 确定哪一模块后,确定当前位置是在当前模块中的第几步。
- 再加上之前走的“模块”的总步数。(若模块是被走慢的,那么很容易想到,走满一个模块的步数是该模块的单位面积)
- 左下:“路径顺时针旋转90度”,即将坐标系顺时针旋转了90度,则就可以理解为横纵坐标的对换。如下图:(这个可能有点抽象,不理解的自己代数值试一下就懂了)。
- 左上就简单了,单纯的是坐标向上平移。
- 右上同左上,只是单纯的平移。
- 右下,要明白坐标系的变换,如下图:
所以,x1=mid- y0 +1 ; y1 = 2n- x0 +1。(mid:中轴线;2n:整个路径拓扑的右边界)
(ps: 这里可以根据同一个点映射到坐标轴的距离不变,这个角度进行考虑)
代码
#include <iostream>
#include <cmath>
using namespace std;
int fun(int n, int x, int y) //给定n天数(迭代次数) 和 位置信息(x,y)
{
int ans = 0; //记录答案
if (n == 1) //第一天--基础 直接按照位置给出返回值
{
if (x == 1 && y == 1)
return 1;
else if (x == 1 && y == 2)
return 2;
else if (x == 2 && y == 2)
return 3;
else if (x == 2 && y == 1)
return 4;
}
else
{
int mid = pow(2, n - 1); // 中轴线位置:整个长度(2^n) 再除以2
if (x <= mid && y <= mid) //确定在哪一个模块
ans = fun(n - 1, y, x); //左下
else if (x <= mid && y > mid)
ans = fun(n - 1, x, y) + mid * mid; //左上 (相对步数+之前模块的步数)(当你走到左上时,左下已走满)
else if (x > mid && y > mid)
ans = fun(n - 1, x, y) + 2 * mid * mid; //右上 (相对步数+之前模块的步数) (当你走到右上时,左下和左上已走满)
else if (x > mid && y <= mid)
ans = fun(n - 1, mid - y + 1, pow(2, n) - x + 1) + 3 * mid * mid; //右下 (相对步数+之前模块的步数) (当你走到右下时,左下、左上、右上 三个模块 已走满)
}
return ans;
}
int main()
{
int n, x, y;
cin >> n >> x >> y;
int re = fun(n, x, y);
cout << re;
system("pause");
return 0;
}
写在最后:若文章存在问题,还请各位大佬批评指正,共同进步。