• 平面最远点对


    平面最近点对可以用分治法在O(n*log(n))时间内解决,平面最远点对可以用凸包+旋转卡壳在O(n*log(n))时间内解决。
    不管是平面最近点对还是平面最远点对,问题都可以避免O(n*n)。但这两种算法都不能直接应用在高维空间。

    POJ2187

    题目链接:POJ2187
    这道题不使用旋转卡壳,在求完凸包之后直接暴力也是可以通过的。

    #include <math.h>
    #include <stdio.h>
    #include <time.h>
    #include <algorithm>
    #include <iostream>
    #include<stdlib.h>
    using namespace std;
    const int MAXN = 5e4;
    struct Point {
    	int x, y;
    } a[MAXN];
    Point *b[MAXN];  //凸包上的顶点
    int N;           //点的总数
    int bi = 0;    //凸包顶点的个数
    int dis(const Point&p, const Point&q) {
    	int dx = p.x - q.x, dy = p.y - q.y;
    	return dx * dx + dy * dy;
    }
    int cross(const Point &x, const Point &y, const Point & z) {
    	//向量xy和向量xz之间的关系,若cross大于0,xz在xy的上方,若小于0,则xz在xy的下方
    	return (x.x - y.x) * (x.y - z.y) - (x.y - y.y) * (x.x - z.x);
    }
    bool comp(const Point &p, const Point &q) {
    	//比较两个点到目标点的角度,如果角度相同,让距离近的点排在前面
    	int c = cross(a[0], p, q);
    	if (c == 0) {
    		return dis(a[0], p) - dis(a[0], q) < 0;
    	}
    	else {
    		return c > 0;
    	}
    }
    void findBag() {
    	//寻找凸包
    	//首先找到最左方、最下方的点
    	for (int i = 1; i < N; i++) {
    		if (a[i].x < a[0].x || (a[i].x == a[0].x && a[i].y < a[0].y)) {
    			swap(a[i], a[0]);
    		}
    	}
    	sort(a + 1, a + N, comp);
    	b[0] = &a[0];
    	b[1] = &a[1];
    	bi = 2;
    	for (int i = 2; i < N; i++) {
    		while (bi > 1 && cross(*b[bi - 2], *b[bi - 1], a[i]) <= 0) {
    			bi--;
    		}
    		b[bi++] = &a[i];
    	}
    }
    int main() {
    	freopen("in.txt", "r", stdin);
    	cin >> N;
    	for (int i = 0; i < N; i++) {
    		scanf("%d%d", &a[i].x, &a[i].y);
    	}
    	if (N == 1) {
    		cout << 0 << endl;
    		return 0;
    	}
    	else if (N == 2) {
    		cout << dis(a[0], a[1]) << endl;
    		return 0;
    	}
    	findBag();
    	int ans = 0;
    	for (int i = 0; i < bi; i++) {
    		for (int j = i + 1; j < bi; j++) {
    			ans = max(ans, dis(*b[i], *b[j]));
    		}
    	}
    	cout << ans << endl;
    	return 0;
    }
    

    凸包问题的五种解法

    暴力算法

    对于任意一对顶点A,B,需要O(n)复杂度验证其余顶点是否在它的同一侧,若是,则A、B是凸包的两个顶点,AB构成凸多边形的一条边。整个算法复杂度为O(n^3)

    分治法

    首先找到最左最下点A、最右最上点B,AB两点必为凸包上的两点,并且AB把整个凸包一分为二,称上下两个凸包为:上凸包和下凸包。接下来在上凸包和下凸包中寻找凸包点。
    在上凸包中,找到离直线A、B最远的那个点,记为C,此点必然也是凸包顶点。直线AC上方的点成为左凸包,直线BC上方的点称为右凸包,继续分治即可找到全部顶点。
    复杂度为O(n*log(n))

    Graham扫描法

    首先找到平面中的最左、最下点A,然后将其余点进行排序,排序的标准就是A点和该点的连线的斜率。
    对排序之后的点使用一个栈进行遍历,栈顶两点的连线成为栈顶向量。坚守一个法则:即将入栈的顶点一定在栈顶向量的上方。如果不满足此法则,就要弹出栈顶元素。
    最终得到的栈中元素就是凸包的顶点。
    Grapham算法中遍历顶点复杂度为O(n),主要复杂度集中在顶点排序上,复杂度为O(n*log(n))。

    Jarvis步进法

    时间复杂度:O(nH)。(其中 n 是点的总个数,H 是凸包上的点的个数)
    思路:

    纵坐标最小的那个点一定是凸包上的点,例如图上的 P0。
    从 P0 开始,按逆时针的方向,逐个找凸包上的点,每前进一步找到一个点,所以叫作步进法。
    怎么找下一个点呢?利用夹角。假设现在已经找到 {P0,P1,P2} 了,要找下一个点:剩下的点分别和 P2 组成向量,设这个向量与向量P1P2的夹角为 β 。当 β 最小时就是所要求的下一个点了,此处为 P3 。

    Melkman算法

    Melkman算法又称“快包”算法,它是一种在线算法。
    1887年,Melkman的凸包算法不再像Graham算法那样使用栈实现,而是使用双向链表,使得新加入的顶点既可以放到左边又可以放到右边,这样就一举避免了O(nlog(n))的排序操作,复杂度直接降为O(n)了。并且,此算法是在线的。
    一般情况下,在线算法比离线算法复杂度要高,因为在线算法的条件更加苛刻。但Melkman算法既是在线算法复杂度又低于以往的凸包算法。

    #include<stdio.h>
    #include<stdlib.h>
    
    int g_result[240][2];
    
    /*getResult()实现功能:以坐标P0(x1,y1)和Pn(x2,y2)为直线,找出pack里面里这条直线最远的点Pmax
    *并找出直线P0Pmax和PmaxPn的上包,进行递归。
    *注:Pack[0][0]存放点的个数,pack[1]开始存放点的坐标。
    *全局变量g_result[][]用来存放凸包上的点,即最终所要的答案。同样g_result[0][0]存放的是已找到的点的个数。
    **/
    void getResult(int Pack[240][2], int x1, int y1, int x2, int y2)
    {
        int i,t,x3,y3,R,Rmax,tmax;
        int ResultPack[240][2];
        ResultPack[0][0] = 0;
        if(Pack[0][0] <= 1)
            return; 
        x3 = Pack[1][0];
        y3 = Pack[1][1];
        R = x1*y2 + x3*y1 + x2*y3 - x3*y2 - x2*y1 - x1*y3;
        Rmax = R;
        tmax = 1;
        for(i=2;i<=Pack[0][0];i++)
        {
            x3 = Pack[i][0];
            y3 = Pack[i][1];
            R = x1*y2 + x3*y1 + x2*y3 - x3*y2 - x2*y1 - x1*y3;
            if(R >= 0)
            {
                t = ++ResultPack[0][0];
                ResultPack[t][0] = x3;
                ResultPack[t][1] = y3;
            }
            if(R > Rmax)
            {
                Rmax = R;
                tmax = i;
            }
        }
        if(Rmax <= 0)
        {
            for(i=1;i<ResultPack[0][0];i++)
            {
                x3 = ResultPack[i][0];
                y3 = ResultPack[i][1];
                R = x1*y2 + x3*y1 + x2*y3 - x3*y2 - x2*y1 - x1*y3;
                if(R == 0 && !((x3==x2&&y3==y2)||(x3==x1&&y3==y1)))
                {
                    t = ++g_result[0][0];
                    g_result[t][0] = ResultPack[i][0];
                    g_result[t][1] = ResultPack[i][1];
                }
            }
            return;
        }
        else
        {
            t = ++g_result[0][0];
            g_result[t][0] = Pack[tmax][0];
            g_result[t][1] = Pack[tmax][1];
            if(ResultPack[0][0] == 0)
                return;
        }
        getResult(ResultPack,x1,y1,Pack[tmax][0],Pack[tmax][1]);
        getResult(ResultPack,Pack[tmax][0],Pack[tmax][1],x2,y2);
    }
    
    void main()
    {
        int Point[240][2];//Point存所有点。
        int i=1;
        int x1,y1,x2,y2,x3,y3;
        g_result[0][0]=0;Point[0][0]=0;//Point的第一行第一列元素存放包里面有几个点。初始化为0。
        printf("请输入所有点的坐标:
    ");
        while(scanf("%d,%d",&Point[i][0],&Point[i][1]) != EOF)
            i++;
        Point[0][0] = i-1;
        x1 = Point[1][0];
        y1 = Point[1][1];
        x2 = x1;
        y2 = y1;
        for(i=2;i<=Point[0][0];i++)
        {
            x3 = Point[i][0];
            y3 = Point[i][1];
            if(x3 < x1)
            {
                x1 = x3;
                y1 = y3;
            }
            else if(x3 > x2)
            {
                x2 = x3;
                y2 = y3;
            }
        }
        g_result[1][0] = x1;
        g_result[1][1] = y1;
        g_result[2][0] = x2;
        g_result[2][1] = y2;
        g_result[0][0] += 2;
        getResult(Point, x1, y1, x2, y2);
        getResult(Point, x2, y2, x1, y1);
    
        printf("
    
    构成凸包的点有:
    ");
        for(i=1;i<=g_result[0][0];i++)
            printf("(%d,%d)
    ",g_result[i][0],g_result[i][1]);
        system("pause");
    }
    

    旋转卡壳算法

    在得到凸包以后,可以只在顶点上面找最远点了。同样,如果不O(n^2)两两枚举,可以想象有两条平行线, “卡”住这个凸包,然后卡紧的情况下旋转一圈,肯定就能找到凸包直径,也就找到了最远的点对。或许这就是为啥叫“旋转卡壳法”。
    枚举凸包上的所有边,对每一条边找出凸包上离该边最远的顶点,计算这个顶点到该边两个端点的距离,并记录最大的值。直观上这是一个O(n2)的算法,和直接枚举任意两个顶点一样了。但是注意到当我们逆时针枚举边的时候,最远点的变化也是逆时针的,这样就可以不用从头计算最远点,而可以紧接着上一次的最远点继续计算,于是我们得到了O(n)的算法。

    参考资料

    凸包问题的五种解法
    http://www.cnblogs.com/jbelial/archive/2011/08/05/2128625.html

  • 相关阅读:
    TensorFlow_CNN_MNIST遇到的问题
    TensorFlow_CNN_MNIST问题
    TensorFlow_CNN内tf.nn.max_pool和tf.layers.max_pooling2d参数
    TensorFlow_CNN内tf.nn.conv2d和tf.layers.conv2d参数
    mysql 更新语句中加判断条件
    数据库 数据去重并取id最大的数据sql
    elasticsearch------java操作之QueryBuilders构建搜索Query
    Elasticsearch java api 基本搜索部分详解
    java 连接 elasticsearch 报错java.lang.NoClassDefFoundError: org/apache/http/auth/Credentials 解决
    java 获取文件内所有文件名
  • 原文地址:https://www.cnblogs.com/weiyinfu/p/10659785.html
Copyright © 2020-2023  润新知