import java.util.ArrayList;
import java.util.List;
/**
* 题目来源:https://segmentfault.com/q/1010000000519081
*
* 琼斯博士在寻宝的过程中,来到了一个平面图呈矩形的封闭房间。
*
* 矩形的宽度为w,高度为h。为方便描述,我们将矩形左上角坐标定为(0,0),右下角坐标定为(w,h)。
*
* 房间的入口在矩形上沿的中点(即(w/2,0)),出口在矩形下沿的中点(即(w/2,h))。
* 狡猾的魔王还旋转了许多红外深测器,一旦进入探测器的探测半径以内,将触发警报。
*
* 现在琼斯博士向您求助,他能否全身而退不惊动魔王?
*
* 输入:boolean escape(int w,int h,int n,double[] x,double[] y,double[] r)
*
* w为矩形宽度,h为矩形高度,n为探测器总数。每i个探测器的坐标为(x[i],y[i]),探测半径为r[i].
*
* 输出:true or false
*
* PS:这题其实没说琼斯博士多胖哈~
*
*/
public class Test {
/** 含有左边界的探测器对象的集合 */
private static List<Detector> listDetectors = new ArrayList<Detector>();
/** 当前探测器是否含有左边界 */
private static boolean nowIsLift = false;
/** 是否含有右边界探测器 */
private static boolean isRight = false;
/** 最终找没找到 */
private static boolean result;
/**
* 解决原理:
*
* 首先转化为图。每个圆是一个节点,左半个迷宫墙壁是个顶点,右半边迷宫墙壁是个顶点(两个墙壁是对称的U型)。
*
* 如果两个圆相交或者相切、或者碰到墙壁就是节点相连。
*
* 如果左半边墙壁和右半边墙壁联通,则返回false,否则true
*/
public static void main(String[] args) {
// /*测试用例1:门口就有探测器,惊动魔王*/
// int w = 40;
// int h = 40;
// int n = 4;
// double[] xArr = new double[] { 30, 20, 10,20 };
// double[] yArr = new double[] { 10, 20, 30,10 };
// double[] rArr = new double[] { 11, 10, 9,10 };// 11,10,9则最终为false
// /* 测试用例2:探测器左边与右边连通的,惊动魔王 */
// int w = 40;
// int h = 40;
// int n = 4;
// double[] xArr = new double[] { 30, 20, 10, 20 };
// double[] yArr = new double[] { 10, 20, 30, 10 };
// double[] rArr = new double[] { 11, 10, 10, 5 };// 11,10,9则最终为false
/* 测试用例3:探测器左边与右边未连通的,没惊动魔王 */
int w = 40;
int h = 40;
int n = 4;
double[] xArr = new double[] { 30, 20, 10, 20 };
double[] yArr = new double[] { 10, 20, 30, 10 };
double[] rArr = new double[] { 11, 10, 5, 5 };// 11,10,9则最终为false
/* 调用escape判断是否会惊动魔王 */
escape(w, h, n, xArr, yArr, rArr);
/* 打印输出是否会惊动魔王的结果 */
if (result) {
System.out.println("惊动了魔王");
} else {
System.out.println("没惊动魔王");
}
}
/**
*
* @param w
* 距形宽度
* @param h
* 距形高度
* @param n
* 探测器总数
* @param x
* 所有探测器的x坐标集合
* @param y
* 所有探测器的y坐标集合
* @param r
* 所有探测器的半径集合
* @return true:惊动魔王 false:不惊动魔王
*/
private static void escape(int w, int h, int n, double[] xArr,
double[] yArr, double[] rArr) {
/* 初始化数据 */
double x;// 探测器的x坐标
double y;// 探测器的y坐标
double r;// 探测器的半径r
List<Detector> detectorLinks = new ArrayList<Detector>();// 与该探测器相连(相交或者相切)的其它探测器集合
boolean isLeftLink;// 该节点是否与左半边墙壁相连
boolean isRightLink;// 该节点是否与右半边墙壁相连
List<Detector> allDetector = new ArrayList<Detector>();// 所有遍历过的探测器
Detector detector = null;// 临时创建的探测器
/* 遍历每一个探测器 */
for (int i = 0; i < n; i++) {
x = xArr[i];
y = yArr[i];
r = rArr[i];
if (isStartOrEnd(x, y, r, w, h)) {
result = true;// 门口就是探测器,一定惊动魔王
return;
}
isLeftLink = isLinkFun(x, y, r, 0, w, h);
isRightLink = isLinkFun(x, y, r, 1, w, h);
detector = new Detector(x, y, r, detectorLinks, isLeftLink,
isRightLink);
// System.out.println("detector==" + detector.getDetectorLinks());
if (nowIsLift) {
listDetectors.add(detector);
}
setDetectorLinks(detector, allDetector, x, y, r);
allDetector.add(detector);// 开始添加探测器
}
/* 计算每个有左边界的探测器开始深度遍历 */
if (isRight) {
/* 右边界存在开始查找 */
resultDepthTraverse();
} else {
/* 右边界不存在 */
result = false;
}
}
/**
*
* @param x
* 当前探测器x坐标
* @param y
* 当前探测器y坐标
* @param r
* 当前探测器半径r
* @param w
* 当前距形宽
* @param h
* 当前距形高
* @return true:包含了 false:没包含
*/
private static boolean isStartOrEnd(double x, double y, double r, int w,
int h) {
if (Math.pow(Math.abs(x - w / 2), 2) + Math.pow(Math.abs(y - 0), 2) <= Math
.pow(r, 2)) {
/* 包含上面点了 */
return true;
} else if (Math.pow(Math.abs(x - w / 2), 2)
+ Math.pow(Math.abs(y - h), 2) <= Math.pow(r, 2)) {
/* 包含下面点了 */
return true;
}
return false;
}
/**
* 深度遍历
*
* @return 深度遍历结果
*/
private static boolean resultDepthTraverse() {
for (int i = 0; i < listDetectors.size(); i++) {
Detector detector = listDetectors.get(i);// 每一个含有左边界的圆
depthTraverse(detector);// 利用递归深度查找,直到找到一个最终的含有右边界的结果时,就不找了
}
return false;
}
/**
* 利用递归深度查找,直到找到一个最终的含有右边界的结果时,就不找了
*
* @param detector
*/
private static void depthTraverse(Detector detector) {
// System.out.println("==========detector==========");
// System.out.println("x=" + detector.getX() + ",y=" + detector.getY()
// + ",r=" + detector.getR());
if (detector.isTraverse() result) {
return;
}
detector.setTraverse(true);// 下回这个不遍历了
List<Detector> detectors = detector.getDetectorLinks();
for (int i = 0; i < detectors.size(); i++) {
detector = detectors.get(i);
if (detector.isRightLink()) {
// System.out.println("找到了!!!");
result = true;
} else {
depthTraverse(detector);
}
}
}
/**
* 遍历已经遍历过的探测器,判断是否与之相连
*
* @param detectorLinks
* 与该探测器相连(相交或者相切)的其它探测器集合
* @param allDetector
* 所有遍历过的探测器
* @param r
* 探测器的x坐标
* @param y
* 探测器的y坐标
* @param x
* 探测器的半径r
*/
private static void setDetectorLinks(Detector detector,
List<Detector> allDetector, double x, double y, double r) {
List<Detector> detectorLinks = detector.getDetectorLinks(); // 当前新建的探测器的子孩子
Detector allDetectorItem = null;// 每一个曾遍历过的Detector
for (int i = 0; i < allDetector.size(); i++) {
allDetectorItem = allDetector.get(i);
double itemX = allDetectorItem.getX();
double itemY = allDetectorItem.getY();
double itemR = allDetectorItem.getR();
if (Math.pow(Math.abs(itemX - x), 2)
+ Math.pow(Math.abs(itemY - y), 2) <= Math
.pow(itemR + r, 2)) {
/* 相连 */
// System.out.println("allDetectorItem= " + allDetectorItem);
// System.out.println("getDetectorLinks= "
// + allDetectorItem.getDetectorLinks());
allDetectorItem.getDetectorLinks().add(detector);// 当前探测器加入可连接探测器
detectorLinks.add(allDetectorItem);// 可连接探测器加入当前探测器
} else {
/* 不相连不处理 */
}
}
}
/**
* 是否挨着边界
*
* @param x
* 当前探测器x坐标
* @param y
* 当前探测器y坐标
* @param r
* 当前探测器半径r
* @param direction
* 0:左 1:右
* @param w
* 总的宽度
* @param h
* 总的高度
* @return 返回是否挨着
*/
private static boolean isLinkFun(double x, double y, double r,
int direction, int w, int h) {
if (direction == 0) {
/* 左边界判断 */
if (((y - r <= 0 y + r >= h) && x < w / 2) x - r <= 0) {
/* 挨着 */
nowIsLift = true;
return true;
} else {
/* 不挨着,默认执行最后的false */
nowIsLift = false;
}
} else if (direction == 1) {
/* 右边界判断 */
if (((y - r <= 0 y + r >= h) && x > w / 2) x + r >= w) {
/* 挨着 */
isRight = true;// 就执行这一次就够了,我只想知道有没有右边界的存在
return true;
} else {
/* 不挨着,默认执行最后的false */
}
}
return false;
}
}
/**
* 探测器模型类
*/
class Detector {
/** 探测器的x坐标 */
private double x;
/** 探测器的y坐标 */
private double y;
/** 探测器的半径r */
private double r;
/** 与该探测器相连(相交或者相切)的其它探测器集合 */
private List<Detector> detectorLinks;
/** 该节点是否与左半边墙壁相连 */
private boolean isLeftLink;
/** 该节点是否与右半边墙壁相连 */
private boolean isRightLink;
/** 当前结点是否被遍历过 */
private boolean isTraverse = false;
/**
* 通过构造方法初始化该探测器的值
*
* @param x
* 初始化探测器的x坐标
* @param y
* 初始化探测器的y坐标
* @param r
* 初始化探测器的半径r
* @param detectorLinks
* 初始化与该探测器相连(相交或者相切)的其它探测器集合
* @param isLeftLink
* 初始化该节点是否与左半边墙壁相连
* @param isRightLink
* 初始化该节点是否与右半边墙壁相连
*/
public Detector(double x, double y, double r, List<Detector> detectorLinks,
boolean isLeftLink, boolean isRightLink) {
super();
this.x = x;
this.y = y;
this.r = r;
this.detectorLinks = detectorLinks;
this.isLeftLink = isLeftLink;
this.isRightLink = isRightLink;
}
/* ======get、set方法====== */
public double getX() {
return x;
}
public void setX(double x) {
this.x = x;
}
public double getY() {
return y;
}
public void setY(double y) {
this.y = y;
}
public double getR() {
return r;
}
public boolean isTraverse() {
return isTraverse;
}
public void setTraverse(boolean isTraverse) {
this.isTraverse = isTraverse;
}
public void setR(double r) {
this.r = r;
}
public List<Detector> getDetectorLinks() {
return detectorLinks;
}
public void setDetectorLinks(List<Detector> detectorLinks) {
this.detectorLinks = detectorLinks;
}
public boolean isLeftLink() {
return isLeftLink;
}
public void setLeftLink(boolean isLeftLink) {
this.isLeftLink = isLeftLink;
}
public boolean isRightLink() {
return isRightLink;
}
public void setRightLink(boolean isRightLink) {
this.isRightLink = isRightLink;
}
}
点击查看更多内容
2人点赞
评论
共同学习,写下你的评论
评论加载中...
作者其他优质文章
正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦