java魔王_算法_java_魔王深测器

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 listDetectors = new ArrayList();

/** 当前探测器是否含有左边界 */

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 detectorLinks = new ArrayList();// 与该探测器相连(相交或者相切)的其它探测器集合

boolean isLeftLink;// 该节点是否与左半边墙壁相连

boolean isRightLink;// 该节点是否与右半边墙壁相连

List allDetector = new ArrayList();// 所有遍历过的探测器

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 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 allDetector, double x, double y, double r) {

List 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 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 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 getDetectorLinks() {

return detectorLinks;

}

public void setDetectorLinks(List 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;

}

}

AAffA0nNPuCLAAAAAElFTkSuQmCC

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值