• 算法:poj1066 宝藏猎人问题。


    package practice;
    
    import java.util.Scanner;
    
    public class TreasureHunt {
    
        public static void main(String[] args) {
            Scanner cin = new Scanner(System.in);
            int[][] walls;
            float x, y;
            int doors = Integer.MAX_VALUE, temp1, temp2;
            int n = cin.nextInt();
            walls = new int[n][4];
            for (int j = 0; j < n; j++) {
                walls[j][0] = cin.nextInt();
                walls[j][1] = cin.nextInt();
                walls[j][2] = cin.nextInt();
                walls[j][3] = cin.nextInt();
            }
            x = cin.nextFloat();
            y = cin.nextFloat();
            for (int j = 0; j < n; j++) {
                temp1 = find(walls[j][0], walls[j][1], x, y, walls, j);
                temp2 = find(walls[j][2], walls[j][3], x, y, walls, j);
                doors = temp1 < temp2 ? (temp1 < doors ? temp1 : doors)
                        : (temp2 < doors ? temp2 : doors);
            }
            if (n == 0)
                doors = 0;
            doors++;
            System.out.println("Number of doors = " + doors);
        }
    
        private static int find(int x1, int y2, float x, float y, int[][] walls,
                int j) {
            int count = 0;
            for (int i = 0, len = walls.length; i < len; i++) {
                if (i == j)
                    continue;
                if (isIntersect(x1, y2, x, y, walls[i]))
                    count++;
            }
            return count;
        }
    
        /**
         * 
         * 跨立实验
         */
        private static boolean isIntersect(int startX, int startY, float endX,
                float endY, int[] wall) {
            if ((Math.max(startX, endX) >= Math.min(wall[0], wall[2]))
                    && (Math.max(wall[0], wall[2]) >= Math.min(startX, endX))
                    && (Math.max(startY, endY) >= Math.min(wall[1], wall[3]))
                    && (Math.max(wall[1], wall[3]) >= Math.min(startY, endY))
                    && (multiply(wall[0], wall[1], endX, endY, startX, startY)
                            * multiply(endX, endY, wall[2], wall[3], startX, startY) > 0)
                    && (multiply(startX, startY, wall[2], wall[3], wall[0], wall[1])
                            * multiply(wall[2], wall[3], endX, endY, wall[0],
                                    wall[1]) > 0))
                return true;
            else
                return false;
        }
    
        private static double multiply(float x1, float y1, float x2, float y2,
                float x3, float y3) {
            return ((x1 - x3) * (y2 - y3) - (x2 - x3) * (y1 - y3));
        }
    
    }
    点我展开代码

    该问题是:

    一伙寻宝人探测到了埃及金字塔底层的宝藏,但是宝藏被n堵墙围着,如果要爆破,只能在每堵墙的中点开门。现在问题来了,对于随机给定的n堵墙,算出最少需要的开门数。

    输入数据如下所示,第一行的整数n表示墙数,后面的n行表示墙体两端坐标,以金字塔边缘左下角为(0,0)右上角为(100,100),每行的四个数据分别为(x1,y1,x2,y2)

    最后一行的数据表示宝藏点P的位置。

    7
    20 0 37 100
    40 0 76 100
    85 0 0 75
    100 90 0 90
    0 71 100 61
    0 14 100 38
    100 47 47 100
    54.5 55.4

    算法的思路很简单(但是实现有点儿问题,比较耗时,正在改进中):

    用给出的所有点对宝藏点P做线段,找出交点最少的那一组,既是最小开门数。

    以上结论需要证明多个推论,证明过程如下:

    1、对于任意P,如果和P只间隔一堵墙,那么,P只需要开一扇门

    证明:无需证明显而易见。

    结论:只要证明墙数,就可以证明门数。

    2、边缘上的任意一中点P0对P做线段,交点数等于P到Pi间隔的最小墙数

    证明:

      连接P0-P,交点设为N。

      设,存在一条开门路径,连接P0到P,中间穿过了M堵墙。

      可知,P0-P与开门路径之间是封闭的多边形(路径是直线且与P0-P重合不讨论)。

      有定理一:最小完整路径不会两次闯过同一堵墙。(如果有两次闯过,则至少有三个交点ABC,连接A-C,则路径更少,易证)

      有定理二:两条子路径之间有且仅有一堵墙(易证,穿过墙只能抵达对侧,不能抵达同侧)

      由定理二可知,墙一定会经过多边形内部,由定理一可知,墙不会再次经过路径,则,墙一定会经过P0-P

      即是M<=N

      

      又有公理一:两条线段最多只有一个交点

      经过了P0-P的线段一定和封闭多边形相交。(由于线段端点落在金字塔边缘,易证)

      即N<=M

      所以M=N。

      

      

      

    3、任意现存线段端点与P连线,交点数等于 邻近两个中点分别与P连线 的交点数 中的小值

    证明:

      设有两线段L1,L2分别交金字塔外墙为P1,P2,且P1、P2之间再无其他现存线段的交点

      设有一点Pn处于P1,P2之间

      对Pn-P做连线,设有一现存直线Lx,与L1相交,但不与Pn-P相交,则此时满足Pn对P的交点数小于P1。

      此时,Lx与P1-P2外墙的交点,必然落在Pn和P1之间(此点易证),与P1、P2之间无现存线段交点违背。

      可证,Pn-P的交点数一定大于等于P1-P。

      同理可证Pn和P2关系。

      当等于的时候,Pn与P1等价。

      

      当大于的时候,推论如下:

      设有一点Px,Px在Pn-P1延长线上,为P1-P0(P0为另一相交点或者金字塔顶点,相交点等于顶点情况另行讨论)线段上的任意某点。

      

      可知,Px和P1之间再无任意交点。

      那么,沿用先前的推论,P1-P交点数大于等于Px-P0交点数。

      当大于的时候,必有一线段与P1-P相交,不与Px-P0相交,此线段不能落在P1-Px段,只能落在P1-Pn,且不为P1本身(相交非重合),与P1-Pn之间无交点违背。

      所以,必然是等于关系(同理可得L1即是Pn-P多出的那一相交线)

      

      相交点等于顶点情况,如果有P点同侧相交线,相交线必然与金字塔边缘有两个交点,无论落点如何,都能多次引用前半部分推论来同理证得。

      证毕。

     结论:

      只需要求线段端点与P的交点数,即可得到最小值。

       

  • 相关阅读:
    一条命令深度清理你的mac
    将以太坊封装为 ERC20
    golang subprocess tests
    go 笔记
    readme
    如何使用 channel
    修改vscode caipeiyu.writeCnblog ,简化博客发布
    thedao
    firefox 59 无法使用 pac 代理上网
    scrapy简单使用
  • 原文地址:https://www.cnblogs.com/anrainie/p/4049844.html
Copyright © 2020-2023  润新知