HDU1392(凸包)

题目

There are a lot of trees in an area. A peasant wants to buy a rope to surround all these trees. So at first he must know the minimal required length of the rope. However, he does not know how to calculate it. Can you help him?
The diameter and length of the trees are omitted, which means a tree can be seen as a point. The thickness of the rope is also omitted which means a rope can be seen as a line.
HDU1392(凸包)_第1张图片
Input

The input contains one or more data sets. At first line of each input data set is number of trees in this data set, it is followed by series of coordinates of the trees. Each coordinate is a positive integer pair, and each integer is less than 32767. Each pair is separated by blank.
Zero at line for number of trees terminates the input for your program.

Output

The minimal length of the rope. The precision should be 10^-2.

Sample Input

9
12 7
24 9
30 5
41 9
80 7
50 87
22 9
45 1
50 7
0

Sample Output

243.06

意思

就是在平面给你一组点集,让你求出周长最小的多边形可以包围住全部的点。
题目翻译为给你一个区域的树,问买多长的绳子可以将它们包围起来。

思路

此题标准解法为求凸包,求出凸包后,然后求这些点之间的距离之和即为所需绳子的长度。下面简单解释下概念。

相关概念

凸包

严格的数学证明为:在一个实数向量空间V中,对于给定集合X,所有包含X的凸集的交集S被称为X的凸包。

所谓凸集,就是对于集合中任意两点x,y,若存在a属于[0,1],ax + (1 - a)y 还属于这个集合,即任意两点的线段上的点也属于这个集合,这个集合就是凸集。

我们还可以将凸包映射到几何上去理解,在一个二维平面,凸包可以想象为一个刚好包含所有点的橡皮圈。计算凸包其实就是计算求的外围(蓝线)上的点。

向量积

向量积又称叉乘,外积。向量积的结果还是一个向量。

\vec{x} \times \vec{y} = |x||y|sinO \vec{n}

其中O为向量x与向量y之间的夹角,n为垂直于向量x与向量y所在的平面的单位向量。
向量积的结果为法向量,该向量垂直于x与y向量构成的平面。

Andrew算法

算法步骤为:
1. 首先排序所有的点,主关键字为x,副关键字为y
2. 将第一个点,第二点加入凸包栈中
3. 从第三个点开始,判断这个点是否在当前凸包的前进方向,也就是栈顶两个点构成向量的左边,若是则继续
4. 否则,则会依次弹出栈顶顶点,重复第3步,直到当前的点符合前进的方向。
5. 考察所有点,这样我们找到只是凸包的下边界,所以从最后一个点开始,倒着再来一遍,得到上边界。
6. 这样,我们就得到了凸包的所有的点

为什么要用到向量积呢,因为通过向量积结果的正负性,可以判断新加入的点位于一个向量的左边还是右边,这样就可以更好的把外围的点加进来。 如图:

为什么是左边为前进方向,因为如果位于左边,必然可以肯定这是外围边,自己画图就可以了,而如果位于右边,则可能栈顶的点位于栈顶第二点和待考察点的连线之内,这样栈顶点就不是多边形的边界了!所以要弹出栈,保证新加入点都是本次的外界点。

代码

package com.special.HDU;

import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner;


/**
 * Created by Special on 2018/5/18 13:45
 */
public class HDU1392 {

    static class Node{
        int x, y;
        public Node(int x, int y){
            this.x = x;
            this.y = y;
        }

        /**
         * 两点形成一个向量
         * @param node
         * @return
         */
        public Node vector(Node node){
            return new Node(this.x - node.x, this.y - node.y);
        }
    }

    /**
     * 向量积
     * @param o1
     * @param o2
     * @return
     */
    static int crossProduct(Node o1, Node o2){
        return o1.x * o2.y - o1.y * o2.x;
    }

    /**
     * 欧式距离
     * @param o1
     * @param o2
     * @return
     */
    static double distance(Node o1, Node o2){
        return Math.sqrt(Math.pow(o1.x - o2.x, 2) + Math.pow(o1.y - o2.y, 2));
    }

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int n;
        while(input.hasNext()) {
            n = input.nextInt();
            if (n == 0) {
                break;
            }
            Node[] nodes = new Node[n];
            Node[] emboss = new Node[2 * n];
            for (int i = 0; i < n; i++) {
                nodes[i] = new Node(input.nextInt(), input.nextInt());
            }
            if (n == 2) { //坑爹的题目
                System.out.println(String.format("%.2f", distance(nodes[0], nodes[1])));
            } else {
                Arrays.sort(nodes, new Comparator() {
                    @Override
                    public int compare(Node o1, Node o2) {
                        if (o1.x != o2.x) {
                            return o1.x - o2.x;
                        } else {
                            return o1.y - o2.y;
                        }
                    }
                });
                int m = 0;
                for (int i = 0; i < n; i++) { //下边界
                    while (m > 1 && (crossProduct(emboss[m - 1].vector(emboss[m - 2]), nodes[i].vector(emboss[m - 2])) < 0)) {
                        m--;
                    }
                    emboss[m++] = nodes[i];
                }
                int k = m;
                for (int i = n - 2; i >= 0; i--) { //上边界
                    while (m > k && (crossProduct(emboss[m - 1].vector(emboss[m - 2]), nodes[i].vector(emboss[m - 2])) < 0)) {
                        m--;
                    }
                    emboss[m++] = nodes[i];
                }
                double sum = 0;
                //注意这里凸包里最后一个已经是第一个点了,所以最后不用单独求最后一点与最开始点的距离
                for (int i = 1; i < m; i++) {
                    sum += distance(emboss[i], emboss[i - 1]);
                }
                System.out.println(String.format("%.2f", sum));
            }
        }
    }
}

你可能感兴趣的:(不刷题心里难受,凸包)