POJ1556 The Doors

一.原题链接:

http://poj.org/problem?id=1556

二.题目大意:

  在一个 10×10 的坐标系上,左下角是原点,求从 (0,5)>(10,5) 的最短路径,坐标系有竖直的墙,墙不可直接穿越,但每面墙有2个出口可供通过。

三.解题思路:
  1. 将每面墙看成3段线段,将每个可达的点作为图的一个顶点。
    • 明确肯定是贴着线段端点走的,否则根据三角形法则会产生更短的路线。因此可达的点就是线段不贴墙的端点还有起点和终点。
    • 图的顶点两两匹配看是否到达,建图,求最短路径。是否到达评判标准就是扫描存在所有线段,看顶点组成的线段与存在所有墙构成的线段是否相交。
  2. 判断线段相交:
    定理:设线段 AB CD 所在直线分别为 AB CD ,若 AB CD 相交并且 CD AB 相交,则线段 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)
      四个中有一个成立,则不相交。
  3. 但是这题有些在端点相交的的情况是可以连线的,比如要连的线段(图的边)的起点或终点就是属于墙的线段的端点,这样按上述判断的话是相交,也就是无法生成边,但是实际中是可以的。
    因此快速排斥实验不能直接使用,先利用跨立实验得出的叉乘结果判断是否共线:
    • 若不是共线的相交( 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值