一.原题链接:
http://poj.org/problem?id=1556
二.题目大意:
在一个 10×10 的坐标系上,左下角是原点,求从 (0,5)−>(10,5) 的最短路径,坐标系有竖直的墙,墙不可直接穿越,但每面墙有2个出口可供通过。
三.解题思路:
- 将每面墙看成3段线段,将每个可达的点作为图的一个顶点。
- 明确肯定是贴着线段端点走的,否则根据三角形法则会产生更短的路线。因此可达的点就是线段不贴墙的端点还有起点和终点。
- 图的顶点两两匹配看是否到达,建图,求最短路径。是否到达评判标准就是扫描存在所有线段,看顶点组成的线段与存在所有墙构成的线段是否相交。
- 判断线段相交:
定理:设线段 AB 与 CD 所在直线分别为 A′B′ 和 C′D′ ,若 AB 与 C′D′ 相交并且 CD 与 A′B′ 相交,则线段 AB 与 CD 相交。
- 判断线段与直线相交可使用这题目所用的方法:
若 AC→×AD→ 与 BC→×BD→ 符号不同,并且, CA→×CB→ 与 DB→×DA→ 符号不同,则他们相交,并且交点不在端点上。 (跨立实验) - 但是若交点在线段端点或者线段共线(叉乘等于0)则要重新讨论是否相交:
设 Nx 与 Ny 分别为点 N 的x 与 y 坐标。通过四次判断(快速排斥实验),可去除在线段端点或者共线情况。若四个中有一个成立,则不相交。⎧⎩⎨⎪⎪⎪⎪⎪⎪⎪⎪min(Ax,Bx)>max(Cx,Dx)min(Ay,By)>max(Cy,Dy)min(Cx,Dx)>max(Ax,Bx)min(Cx,Dx)>max(Ax,Bx)
- 判断线段与直线相交可使用这题目所用的方法:
- 但是这题有些在端点相交的的情况是可以连线的,比如要连的线段(图的边)的起点或终点就是属于墙的线段的端点,这样按上述判断的话是相交,也就是无法生成边,但是实际中是可以的。
因此快速排斥实验不能直接使用,先利用跨立实验得出的叉乘结果判断是否共线:
- 若不是共线的相交( AC→×AD→=0 与 BC→×BD→=0 不同时成立),马上判断顶点直接可以存在边。
- 若是共线的相交( AC→×AD→=0 与 BC→×BD→=0 同时成立),此时( CA→×CB→=0 与 DA→×DB→=0 肯定也同时成立,通过判断 y <script type="math/tex" id="MathJax-Element-515">y</script> 值来判断是否存在边。
四.代码:
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
class Main {
static final int MAX_SIZE = 50;
static final double INF = 1e40;
static final double ESP = 1e-4;
static int wallNum;
static Segment[] segments = new Segment[4*MAX_SIZE];
static Node[] nodes = new Node[4*MAX_SIZE];
static List<EdgeNode>[] graph = new ArrayList[4*MAX_SIZE];
static {
for (int i = 0; i < 4*MAX_SIZE; i++){
nodes[i] = new Node();
graph[i] = new ArrayList<EdgeNode>();
segments[i] = new Segment();
}
}
//输入向量(x1, y1), (x2, y2)
static double crossProduct(double x1, double y1, double x2, double y2){
return x1*y2- x2*y1;
}
static boolean segmentIntersect(Segment s1, Segment s2){
Point a1 = s1.a, b1 = s1.b, a2 = s2.a, b2 = s2.b;
Vector a1a2 = new Vector(), a1b2 = new Vector(),
b1a2 = new Vector(), b1b2 = new Vector();
a1a2.setXY(a2.x-a1.x, a2.y-a1.y);
a1b2.setXY(b2.x-a1.x, b2.y-a1.y);
b1a2.setXY(a2.x-b1.x, a2.y-b1.y);
b1b2.setXY(b2.x-b1.x, b2.y-b1.y);
Vector a2a1 = a1a2.reVector(), b2a1 = a1b2.reVector(),
a2b1 = b1a2.reVector(), b2b1 = b1b2.reVector();
double cross1 = crossProduct(a1a2.x, a1a2.y, a1b2.x, a1b2.y),
cross2 = crossProduct(b1a2.x, b1a2.y, b1b2.x, b1b2.y),
cross3 = crossProduct(a2a1.x, a2a1.y, a2b1.x, a2b1.y),
cross4 = crossProduct(b2a1.x, b2a1.y, b2b1.x, b2b1.y);
if (cross1*cross2 < 0 && cross3*cross4 < 0){
return true;
} else if (Math.abs(cross1) < ESP && Math.abs(cross2) < ESP){
if(Math.min(a1.y, b1.y) < Math.max(a2.y, b2.y) &&
Math.min(a2.y, b2.y) < Math.max(a1.y, b1.y)){
return true;
}
return false;
}
return false;
}
//Node n, m是否有边, 存在边(之间无线段)返回真
static boolean existEdge(int n, int m) {
Segment edge = new Segment();
edge.a.setXY(nodes[n].pos.x, nodes[n].pos.y);
edge.b.setXY(nodes[m].pos.x, nodes[m].pos.y);
for (int i = 0; i < 3*wallNum; i++) {
if (segmentIntersect(edge, segments[i])){
return false;
}
}
return true;
}
static double getSegmentLength(Point p1, Point p2){
return Math.sqrt((p1.x-p2.x)*(p1.x-p2.x) +
(p1.y-p2.y)*(p1.y-p2.y));
}
static void buildGraph(){
for (int i = 0; i < 4*wallNum+2; i++){
for (int j = i+1; j < 4*wallNum+2; j++){
if (existEdge(i, j)){
double len = getSegmentLength(nodes[i].pos, nodes[j].pos);
EdgeNode temp = new EdgeNode();
temp.id = j;
temp.length = len;
graph[i].add(temp);
temp = new EdgeNode();
temp.id = i;
temp.length = len;
graph[j].add(temp);
}
}
}
}
static double dijkstra(int s, int t){
int nodeNum = 4*wallNum+2, cur = s;
double dist[] = new double[nodeNum];
boolean visited[] = new boolean[nodeNum];
for(int i = 0; i < nodeNum; i++){
if (i == s){
dist[i] = 0;
} else{
dist[i] = INF;
}
visited[i] = false;
}
for(int i = 0; i < graph[s].size(); i++){
dist[graph[s].get(i).id] = graph[s].get(i).length;
}
visited[s] = true;
for(int i = 0; i < nodeNum; i++){
double minLen = INF;
for(int j = 0; j < nodeNum; j++){
if(minLen > dist[j] && !visited[j]){
minLen = dist[j];
cur = j;
}
}
visited[cur] = true;
for(int j = 0; j < graph[cur].size(); j++){
int id = graph[cur].get(j).id;
double len = graph[cur].get(j).length;
if(dist[id] > len + dist[cur] && !visited[id]){
dist[id] = len + dist[cur];
}
}
}
return dist[t];
}
public static void main (String args[]){
Scanner in = new Scanner(System.in);
double x;
double []y = new double[4];
while(true){
wallNum = in.nextInt();
if (wallNum == -1){
break;
}
for (int i = 0; i < 4*MAX_SIZE; i++){
graph[i].removeAll(graph[i]);
}
for (int i = 0; i < wallNum; i++){
x = in.nextDouble();
for (int j = 0; j < 4; j++){
y[j] = in.nextDouble();
nodes[i*4+j].pos.setXY(x, y[j]);
}
segments[i*3].a.setXY(x, 0);
segments[i*3].b.setXY(x, y[0]);
segments[i*3+1].a.setXY(x, y[1]);
segments[i*3+1].b.setXY(x, y[2]);
segments[i*3+2].a.setXY(x, y[3]);
segments[i*3+2].b.setXY(x, 10);
}
nodes[4*wallNum].pos.setXY(0.0, 5.0);
nodes[4*wallNum+1].pos.setXY(10, 5);
buildGraph();
System.out.printf("%.2f\n", dijkstra(4*wallNum, 4*wallNum+1));
}
in.close();
}
}
class Point{
public void setXY(double x, double y) {
this.x = x;
this.y = y;
}
public double x, y;
}
class EdgeNode{
public int id;
public double length;
}
class Node{
public Point pos = new Point();
}
class Segment{
public Point a = new Point();
public Point b = new Point();
}
class Vector{
public void setXY(double x, double y) {
this.x = x;
this.y = y;
}
public Vector reVector() {
Vector v = new Vector();
v.setXY(-x, -y);
return v;
}
public double x, y;
}