题目描述:由正六边形组成的蜂窝小区中,每个正六边形的编号如图所示。求任意2点间的距离。(规定最大编号不超过100000)
真的是Python十行代码搞定,估计C++也是十几行的代码。分析题目,提炼数学模型,再写代码。
这个题可以不用各种复杂的算法,从数学角度解决。用数学思维简化逻辑模型,而不是用计算机逻辑代替数学思维……
四大常用坐标系:笛卡尔直角坐标,极坐标,柱坐标,球坐标。这道题应该采用极坐标!加上等差数列!就够了~
firstValue, secondValue为给的2点,Python代码如下:
1 n_fir = int(math.ceil((1+math.sqrt(1 + 4*(firstValue-1)/3))/2)) 2 n_sec = int(math.ceil((1+math.sqrt(1 + 4*(secondValue-1)/3))/2)) 3 if firstValue == 1: 4 shortestPathLength = n_sec-1 5 elif secondValue == 1: 6 shortestPathLength = n_fir-1 7 else: 8 angle_fir = (n_fir*(n_fir-1)*3+1-firstValue)*2*math.pi/(6*(n_fir-1)) 9 angle_sec = (n_sec*(n_sec-1)*3+1-secondValue)*2*math.pi/(6*(n_sec-1)) 10 shortestPathLength = round(math.sqrt((n_fir-1)**2+(n_sec-1)**2-2*(n_fir-1)*(n_sec-1)*math.cos(angle_fir-angle_sec))) 11 return int(shortestPathLength)
详细解释如下:
极坐标和确定一个点,两点之间的距离公式
图中可以用归纳法总结出等差数列:
圈号(等于极坐标轴长r-1) | 容量 | 最小编号 | 最大编号 |
1 | 1(用0代替) | 1(用0代替) | 1(用0代替) |
2 | 6(6*1) | 2 | 7 |
3 | 12(6*2) | 8 | 19 |
4 | 18(6*3) | 20 | 37 |
5 | 24(6*4) | 38 | 61 |
把第一圈的容量假设为0,最小最大编号假设为0,就是等差数列。(说明我们可以对含有编号1的特殊处理,其他的编号可以一起处理)
数字1、2、3、...、N-1、N,的隐含条件是:1)数字是有秩序(index)的,2)数字的秩序数(index)恰好等于该秩序处数字的值。
等差数列通项公式、求和公式:
这里初值 a1 = 0, 公差d = 6
那么,Sn = ((圈号*(圈号-1))/2)*d + 1; (最后加1是因为我们把a1=0了,实际编号从1开始的,所以这里加上)
给定编号怎么算位于第几圈呢?
对求和公式整理得,n^2 -n -2*Sn/d = 0,把编号赋给Sn,解一元二次方程,因为△>0,所以有解:
根据公式舍弃一个负根,剩下的一个正跟就是解n(一个小数),对n向上取整,就是该编号对应的圈号。
向上取整:把离散的圈号看成连续的,那么(2,3]区间是属于第3圈。
极坐标定位:(设圈号为n, 已知点p)
= n-1
= ((n*(n-1)/2)*d + 1 - p)*π*/(d*(n-1)); π = 3.14...(圆周率),意思就是(第n圈的最大编号-给定的编号)*(圆周率/该圈的容量)!
所以,给定两个点p1, p2,用求出距离,再四舍五入,就是任意2点间的距离!
四舍五入:连接任意两个蜂窝的中心,会发现连线是对称的。
最后,注意下,如果给定的2个点含有1,那么直接返回[另一个的圈号-1]