题目描述:
如图:由正六边形组成的蜂窝小区中,每个正六边形的编号也如图所示。求任意2点间的距离。(规定最大编号不超过100000)
分析:
1、求2点间的距离,第一印象就想到了广度优先算法(广度优先算法简单介绍:如图中的1点找编号为9的点时,可以第一次广度优先找到第二圈的所有元素,第二圈的元素再广度优先可以找到了9号点,所以必有一点可以从1点到9点,且距离为2)。但广度优先算法必须以知道任意点的相邻节点为前提。仔细观察又觉得点与点之间的关联又不是很有规律。
2、很容易看出6个点可以组成一个圆环,如:2,3,4,5,6,7 19,20,38,61,60,36等。为了找出规律,特按照编号顺序进行查找规律,那图看多了容易眼花,特意画了圆环虚线标识一个个圈,然后进行仔细分析;
3、得到所有点的相邻节点的前提是确定所有点的位置,然后再根据位置找规律得出其相邻的节点;
下面说下详细过程:
1、分析如何根据真实编号得到它所对应的圈号即圈内编号:
分析下每个圈的情况(以1号为1圈):
圈号 | 容量 | 最小编号 | 最大编号 |
1 | 1 | 1 | 1 |
2 | 6 | 2 | 7 |
3 | 12 | 8 | 19 |
4 | 18 | 20 | 37 |
1)发现从第2圈开始,元素数量呈等差趋势,依次增加6,容量规律即有:size(n)=6*(n-1) ,同时n>=2;
2)继续观察发现每圈的最小编号减去相邻内圈的最小编号刚好等于内圈的容量即有cycle(n,1)-cycle(n-1,1) = 6*(n-2),同时n>2(第2圈减第一圈规律不成立,n必须大于2),其中cycle(n,1)表示第n圈的第1个元素;
3)根据数列求和可知:
cycle(n,1) -cycle(n-1,1) =6*(n - 2) |
cycle(n-1,1)-cycle(n-2,1) =6*((n-1)- 2) |
cycle(n-2,1)-cycle(n-3,1) =6*((n-2)- 2) |
…… |
cycle(3,1) -cycle(2,1) =6*((3) - 2) |
所以cycle(n,1)=cycle(2,1)+3(n-1)(n-2),由图示1可知cycle(2,1)=2;所以cycle(n,1)=2+3(n-1)(n-2),n>2;当n=2时,cycle(n,1)=2+3*(2-1)*(2-2)=2满足要求,所以cycle(n,1)=2+3(n-1)(n-2)的必需条件为n>=2;
3)由上可以推出:第n圈的m个元素可表示成:cycle(n,m)= 2+3(n-1)(n-2) -1+m,其中1<=m<6*(n-1)且n>=2,当n=1时,cycle(1,1)=1;
至此已经可以推出任意一个真实编号对应的圈号与圈内的编号了:
思路:对给定的任意真实编号与第n圈的极大值比较,若大于则取n+1圈继续比较,小于时停止比较,当前圈号为真实编号对应的圈号,根据cycle(n,m)公式可以计算出m值(圈内编号);
2、分析一个点与所有相邻点之间的关系(直接考虑n>=2的情况,n=1时可做特殊处理):
任意一个点在蜂窝小区中都有6个相邻节点,把它围在中间。
取第3圈的起始点8(第3圈的1号点)的相邻点进行分析,它有2个相邻点在其内圈上,2个点在当前圈,且在它左右;9号点(第3圈的第2点)则只有一个内圈节点,3个相邻外圈节点;
继续分析可知:第4圈的第1、2号点有2个内圈节点,第3号点只有一个内圈节点
……
继续拿第五圈的点进行分析……
可得出如下结论(以第n圈的圈内编号为m的点为例,n>2):
1),当m<n-1时,它有2个内圈节点,圈内编号分别为m-1,m当m=1时,m-1取最大圈内编号,它有2个外圈节点,其圈内编号为m,m+1;
2)当m%n-1等于0时,它只有一个圈内节点,其圈内编号为:m*(n-2)/(n-1),外圈编号为:n+m*n/(n-2),n+m*n/(n-2)+1,n+m*n/(n-2)+2;
3)当m>n-1且m%n-1不等于0时,它有2个圈内节点,其编号可参考比它小的、且有3个内圈节点、且最接近的点,根据偏移来计算,公式如下:
m1= (m-m%(n-1))*(n-3)/(n-1)+m%(n-1)-1,m2=m1+1,其外圈的圈内编号为:w1 = (n-1)+((m-m%(n-1))/(n-1)-1)*n+2+m%(n-1)-1,w2 = w1+1
……
不敢再想一遍了,肯定有优化的空间,欢迎给思路给建议……
思路就说到这了:)
说说实现:
1、定义Position类,根据输入的真实编号转换成对应的圈号及圈内编号,并提供找到所有相邻节点的方法;
2、定义蜂窝类Honeycomb,封装了计算蜂窝最短距离的入口及广度优先搜索算法;
3、辅助工具类HoneycombUtils,封装了几个推导出来的数列公式;
import java.util.HashSet;
import java.util.Set;
/**
* <pre>
* 蜂窝中的一个点
*
* </pre>
*/
public class Position
{
/**
* 圈号
*/
private int cycleNo;
/**
* 圈中的编号
*/
private int columnNo;
/**
* 真实编号
*/
private int realNum;
public Position(int realNum)
{
this.realNum = realNum;
transfer();
}
/**
* <pre>
* 获取其所有的6个相邻点
*
* @return
* </pre>
*/
public Set<Position> getClosestPositions()
{
Set<Position> closestPositions = new HashSet<Position>();
Position nextPosition = null;
Position outerPosition = null;
int outerCycleNo = getCycleNo() + 1;
int outerRealNum = 0;
// 第一圈没有内圈,只有外圈相邻点
if (getCycleNo() == 1)
{
for (int i = 2; i <= 7; i++)
{
nextPosition = new Position(i);
closestPositions.add(nextPosition);
}
return closestPositions;
}
Position innerPosition = null;
int innerRealNum = 0;
int innerCycleNo = getCycleNo() - 1;
// 当前点的列号低于圈号减1时,它与2个相邻的内圈点相邻
if (getColumnNo() < innerCycleNo)
{
// 表示取最后一列(列号最大的那个点)
if (getColumnNo() == 1)
{
innerPosition = getMaxCyclePosition(innerCycleNo);
}
// 否则内圈列号取getColumnNo()-1
else
{
innerRealNum = HoneycombUtils.getMinOfRealNumInCycle(innerCycleNo) - 1 + getColumnNo() - 1;
innerPosition = new Position(innerRealNum);
}
closestPositions.add(innerPosition);
// 添加另一个内圈相邻点
innerRealNum = HoneycombUtils.getMinOfRealNumInCycle(innerCycleNo) - 1 + getColumnNo();
innerPosition = new Position(innerRealNum);
closestPositions.add(innerPosition);
// 添加2个外圈相邻点
outerRealNum = HoneycombUtils.getMinOfRealNumInCycle(outerCycleNo) - 1 + getColumnNo();
outerPosition = new Position(outerRealNum);
closestPositions.add(outerPosition);
closestPositions.add(outerPosition.getNextPosition());
}
// 内环只有一个相邻点
else if (getColumnNo() % innerCycleNo == 0)
{
if (innerCycleNo == 1)
{
innerPosition = new Position(1);
}
else
{
innerRealNum =
HoneycombUtils.getMinOfRealNumInCycle(innerCycleNo) - 1 + getColumnNo() * (innerCycleNo - 1)
/ innerCycleNo;
innerPosition = new Position(innerRealNum);
}
closestPositions.add(innerPosition);
// 添加3个外圈相邻的点
outerRealNum =
HoneycombUtils.getMinOfRealNumInCycle(outerCycleNo) - 1 + innerCycleNo
+ (getColumnNo() / innerCycleNo - 1) * getCycleNo();
outerPosition = new Position(outerRealNum);
closestPositions.add(outerPosition);
nextPosition = outerPosition.getNextPosition();
closestPositions.add(nextPosition);
nextPosition = nextPosition.getNextPosition();
closestPositions.add(nextPosition);
}
// 否则内环有2个相邻点
else
{
// 找出同一圈中列号较小且只有一个相邻点的点
int addColumnNo = getColumnNo() % innerCycleNo;
int criticalColumnNo = getColumnNo() - addColumnNo;
innerRealNum =
HoneycombUtils.getMinOfRealNumInCycle(innerCycleNo) - 1 + criticalColumnNo * (innerCycleNo - 1)
/ innerCycleNo + addColumnNo - 1;
innerPosition = new Position(innerRealNum);
closestPositions.add(innerPosition);
closestPositions.add(innerPosition.getNextPosition());
// 添加另一个外圈点
outerRealNum =
HoneycombUtils.getMinOfRealNumInCycle(outerCycleNo) - 1 + innerCycleNo
+ (criticalColumnNo / innerCycleNo - 1) * getCycleNo() + 2 + addColumnNo - 1;
outerPosition = new Position(outerRealNum);
closestPositions.add(outerPosition);
closestPositions.add(outerPosition.getNextPosition());
}
// 添加本圈中的2个与之相邻的点
closestPositions.add(getLastPosition());
closestPositions.add(getNextPosition());
return closestPositions;
}
/**
* 重载方法
*
* @return
*/
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + realNum;
return result;
}
/**
* 重载方法
*
* @param obj
* @return
*/
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Position other = (Position)obj;
if (realNum != other.realNum)
return false;
return true;
}
/**
* 重载方法
*
* @return
*/
@Override
public String toString()
{
return "Position [realNum=" + realNum + "]";
}
/**
* <pre>
* 把蜂窝上一个点的编号转换成圈号 + 圈内的编号
* </pre>
*/
private void transfer()
{
// 在第一圈
if (getRealNum() == 1)
{
setCycleNo(1);
setColumnNo(1);
}
// 在其它圈
else
{
int cycle = 2;
while (HoneycombUtils.getMaxOfRealNumInCycle(cycle) < getRealNum())
{
cycle++;
}
setCycleNo(cycle);
// 计算真实编号对应圈中的编号
int minCycleNum = HoneycombUtils.getMinOfRealNumInCycle(cycle);
int columnNo = getRealNum() - minCycleNum + 1;
setColumnNo(columnNo);
}
}
/**
* <pre>
* 获取一圈中的下一个位置,如果是最后一个位置,下一个指向第一个;否则列号加1
*
* @return
* </pre>
*/
private Position getNextPosition()
{
// 最后一个的下一个指向圈中的第一个
if (getRealNum() == HoneycombUtils.getMaxOfRealNumInCycle(getCycleNo()))
{
return getMinCyclePosition();
}
else
{
return new Position(getRealNum() + 1);
}
}
/**
* <pre>
* 获取同一圈中列号小1的点
*
* @return
* </pre>
*/
private Position getLastPosition()
{
if (getRealNum() == HoneycombUtils.getMinOfRealNumInCycle(getCycleNo()))
{
return getMaxCyclePosition();
}
else
{
return new Position(getRealNum() - 1);
}
}
/**
* <pre>
* 获取指定圈的最大列号对应的点
*
* @param cycleNo
* @return
* </pre>
*/
private Position getMaxCyclePosition(int cycleNo)
{
return new Position(HoneycombUtils.getMaxOfRealNumInCycle(cycleNo));
}
/**
* <pre>
* 获取当前圈的最大点
*
* @return
* </pre>
*/
private Position getMaxCyclePosition()
{
return getMaxCyclePosition(getCycleNo());
}
/**
* <pre>
* 获取指定圈中最小列号对应的最小点
*
* @return
* </pre>
*/
private Position getMinCyclePosition(int cycleNo)
{
return new Position(HoneycombUtils.getMinOfRealNumInCycle(cycleNo));
}
/**
* <pre>
* 获取当前圈的最小点
*
* @return
* </pre>
*/
private Position getMinCyclePosition()
{
return getMinCyclePosition(getCycleNo());
}
/**
* 获取 cycleNo
*
* @return 返回 cycleNo
*/
private int getCycleNo()
{
return cycleNo;
}
/**
* 获取 columnNo
*
* @return 返回 columnNo
*/
private int getColumnNo()
{
return columnNo;
}
/**
* 获取 realNum
*
* @return 返回 realNum
*/
private int getRealNum()
{
return realNum;
}
/**
* 设置 cycleNo
*
* @param 对cycleNo进行赋值
*/
private void setCycleNo(int cycleNo)
{
this.cycleNo = cycleNo;
}
/**
* 设置 columnNo
*
* @param 对columnNo进行赋值
*/
private void setColumnNo(int columnNo)
{
this.columnNo = columnNo;
}
}
/**
* <pre>
* 蜂窝工具类
*
* </pre>
*/
public final class HoneycombUtils
{
/**
* <默认构造函数>
*/
private HoneycombUtils()
{
}
/**
* <pre>
* 获取蜂窝一圈中的最大真实编号(圈号>=2)
*
* @param cycleNo 圈号
* @return
* </pre>
*/
public static int getMaxOfRealNumInCycle(int cycleNo)
{
return getMinOfRealNumInCycle(cycleNo) + getMaxColumnNumInCycle(cycleNo) - 1;
}
/**
* <pre>
* 获取蜂窝一圈中的最小真实编号
*
* @param cycleNo 圈号
* @return
* </pre>
*/
public static int getMinOfRealNumInCycle(int cycleNo)
{
return 2 + 3 * (cycleNo - 1) * (cycleNo - 2);
}
/**
* <pre>
* 获取一圈中的最大列号(圈号>=2)
*
* @param cycleNo 圈号
* @return
* </pre>
*/
public static int getMaxColumnNumInCycle(int cycleNo)
{
return 6 * (cycleNo - 1);
}
}
import java.util.HashSet;
import java.util.Set;
/**
* <pre>
* 蜂窝
*
* </pre>
*/
public final class Honeycomb
{
private static int count;
private static Set<Position> positions = new HashSet<Position>();
private static Set<Position> closestPositions = new HashSet<Position>();
private Honeycomb()
{
}
/**
* <pre>
* 启动入口,获取蜂窝中2个点的距离
*
* @param startPos 起始位置
* @param endPos 查找目标位置
* @return
* </pre>
*/
public static int getDistance(int startPos, int endPos)
{
if (!check(startPos) || !check(endPos))
{
return -1;
}
Position startPosition = new Position(Math.min(startPos, endPos));
Position endPosition = new Position(Math.max(startPos, endPos));
return getDistance(startPosition, endPosition);
}
private static boolean check(int pos)
{
if (pos <= 0 || pos > 100000)
{
return false;
}
return true;
}
/**
* <pre>
* 初始化
*
* </pre>
*/
private static void init()
{
positions.clear();
count = 0;
closestPositions.clear();
}
/**
* <pre>
* 封装成内部需要的入口
*
* @param startPosition 起始点
* @param endPosition 目标点
* @return
* </pre>
*/
private static int getDistance(Position startPosition, Position endPosition)
{
init();
closestPositions.add(startPosition);
searchNext(endPosition);
return count;
}
/**
* <pre>
* 广度优先遍历,每次取当前点的所有相邻点跟目标点比较,相等就退出,否则一直迭代查找
*
* @param endPosition
* </pre>
*/
private static void searchNext(Position endPosition)
{
if (closestPositions.contains(endPosition))
{
return;
}
Set<Position> copyPositions = new HashSet<Position>(closestPositions.size());
copyPositions.addAll(closestPositions);
closestPositions.clear();
for (Position closestPosition : copyPositions)
{
addClosestPositions(closestPosition.getClosestPositions());
}
count++;
searchNext(endPosition);
}
private static void addClosestPositions(Set<Position> oneOfpositions)
{
for (Position position : oneOfpositions)
{
if (!positions.contains(position))
{
closestPositions.add(position);
}
positions.add(position);
}
}
}
测试类:
PositionTest:验证获取点的相邻节点是否正确:
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.Set;
import org.junit.Test;
/**
* <pre>
* <一句话功能简述>
*
* </pre>
*/
public class PositionTest
{
/**
* case01:极限情况:第一圈的所有相邻节点是否正确
*/
@Test
public void testGetClosestPositions01()
{
Position pos = new Position(1);
Set<Position> positions = pos.getClosestPositions();
assertEquals(positions.size(), 6);
for (int i = 2; i <= 7; i++)
{
assertTrue(positions.contains(new Position(i)));
}
}
/**
* case02:正常情况:第二圈的某点所有相邻节点是否正确
*/
@Test
public void testGetClosestPositions02()
{
Position pos = new Position(2);
Set<Position> positions = pos.getClosestPositions();
assertEquals(positions.size(), 6);
assertTrue(positions.contains(new Position(1)));
assertTrue(positions.contains(new Position(3)));
assertTrue(positions.contains(new Position(7)));
assertTrue(positions.contains(new Position(8)));
assertTrue(positions.contains(new Position(9)));
assertTrue(positions.contains(new Position(10)));
pos = new Position(7);
positions = pos.getClosestPositions();
assertTrue(positions.contains(new Position(1)));
assertTrue(positions.contains(new Position(2)));
assertTrue(positions.contains(new Position(6)));
assertTrue(positions.contains(new Position(8)));
assertTrue(positions.contains(new Position(18)));
assertTrue(positions.contains(new Position(19)));
pos = new Position(6);
positions = pos.getClosestPositions();
assertTrue(positions.contains(new Position(1)));
assertTrue(positions.contains(new Position(5)));
assertTrue(positions.contains(new Position(7)));
assertTrue(positions.contains(new Position(16)));
assertTrue(positions.contains(new Position(17)));
assertTrue(positions.contains(new Position(18)));
}
/**
* case03:正常情况:第三圈的某点所有相邻节点是否正确
*/
@Test
public void testGetClosestPositions03()
{
Position pos = new Position(8);
Set<Position> positions = pos.getClosestPositions();
assertEquals(positions.size(), 6);
assertTrue(positions.contains(new Position(2)));
assertTrue(positions.contains(new Position(7)));
assertTrue(positions.contains(new Position(9)));
assertTrue(positions.contains(new Position(19)));
assertTrue(positions.contains(new Position(20)));
assertTrue(positions.contains(new Position(21)));
pos = new Position(9);
positions = pos.getClosestPositions();
assertTrue(positions.contains(new Position(2)));
assertTrue(positions.contains(new Position(8)));
assertTrue(positions.contains(new Position(10)));
assertTrue(positions.contains(new Position(21)));
assertTrue(positions.contains(new Position(22)));
assertTrue(positions.contains(new Position(23)));
pos = new Position(11);
positions = pos.getClosestPositions();
assertTrue(positions.contains(new Position(3)));
assertTrue(positions.contains(new Position(10)));
assertTrue(positions.contains(new Position(12)));
assertTrue(positions.contains(new Position(24)));
assertTrue(positions.contains(new Position(25)));
assertTrue(positions.contains(new Position(26)));
pos = new Position(12);
positions = pos.getClosestPositions();
assertTrue(positions.contains(new Position(3)));
assertTrue(positions.contains(new Position(4)));
assertTrue(positions.contains(new Position(11)));
assertTrue(positions.contains(new Position(13)));
assertTrue(positions.contains(new Position(27)));
assertTrue(positions.contains(new Position(26)));
pos = new Position(19);
positions = pos.getClosestPositions();
assertTrue(positions.contains(new Position(7)));
assertTrue(positions.contains(new Position(8)));
assertTrue(positions.contains(new Position(18)));
assertTrue(positions.contains(new Position(20)));
assertTrue(positions.contains(new Position(36)));
assertTrue(positions.contains(new Position(37)));
}
/**
* case03:正常情况:第三圈的某点所有相邻节点是否正确
*/
@Test
public void testGetClosestPositions04()
{
Position pos = new Position(20);
Set<Position> positions = pos.getClosestPositions();
assertEquals(positions.size(), 6);
assertTrue(positions.contains(new Position(8)));
assertTrue(positions.contains(new Position(19)));
assertTrue(positions.contains(new Position(21)));
assertTrue(positions.contains(new Position(37)));
assertTrue(positions.contains(new Position(38)));
assertTrue(positions.contains(new Position(39)));
pos = new Position(21);
positions = pos.getClosestPositions();
assertTrue(positions.contains(new Position(9)));
assertTrue(positions.contains(new Position(8)));
assertTrue(positions.contains(new Position(40)));
assertTrue(positions.contains(new Position(20)));
assertTrue(positions.contains(new Position(22)));
assertTrue(positions.contains(new Position(39)));
pos = new Position(25);
positions = pos.getClosestPositions();
assertTrue(positions.contains(new Position(11)));
assertTrue(positions.contains(new Position(24)));
assertTrue(positions.contains(new Position(26)));
assertTrue(positions.contains(new Position(44)));
assertTrue(positions.contains(new Position(45)));
assertTrue(positions.contains(new Position(46)));
pos = new Position(29);
positions = pos.getClosestPositions();
assertTrue(positions.contains(new Position(13)));
assertTrue(positions.contains(new Position(14)));
assertTrue(positions.contains(new Position(28)));
assertTrue(positions.contains(new Position(30)));
assertTrue(positions.contains(new Position(50)));
assertTrue(positions.contains(new Position(51)));
pos = new Position(37);
positions = pos.getClosestPositions();
assertTrue(positions.contains(new Position(19)));
assertTrue(positions.contains(new Position(20)));
assertTrue(positions.contains(new Position(36)));
assertTrue(positions.contains(new Position(38)));
assertTrue(positions.contains(new Position(60)));
assertTrue(positions.contains(new Position(61)));
}
}
HoneycombTest类,验证距离计算是否正确
import static org.junit.Assert.assertEquals;
import org.junit.Test;
/**
* <pre>
* <一句话功能简述>
*
* </pre>
*/
public class HoneycombTest
{
/**
* <pre>
* CASE01:参数非法
*
* </pre>
*/
@Test
public void testGetDistance01()
{
assertEquals(Honeycomb.getDistance(1, 0), -1);
assertEquals(Honeycomb.getDistance(-1, 1), -1);
assertEquals(Honeycomb.getDistance(-1, -1), -1);
}
/**
* <pre>
* CASE02:临界值
*
* </pre>
*/
@Test
public void testGetDistance02()
{
assertEquals(Honeycomb.getDistance(1, 1), 0);
assertEquals(Honeycomb.getDistance(2, 2), 0);
}
/**
* <pre>
* CASE03:正常情况
*
* </pre>
*/
@Test
public void testGetDistance03()
{
assertEquals(Honeycomb.getDistance(2, 40), 3);
assertEquals(Honeycomb.getDistance(49, 61), 8);
assertEquals(Honeycomb.getDistance(50, 44), 5);
assertEquals(Honeycomb.getDistance(47, 59), 8);
assertEquals(Honeycomb.getDistance(49, 57), 8);
assertEquals(Honeycomb.getDistance(50, 44), 5);
assertEquals(Honeycomb.getDistance(100000, 1), 183);
assertEquals(Honeycomb.getDistance(100000, 88888), 355);
}
}