zoukankan      html  css  js  c++  java
  • 【算法学习笔记】32.计算几何 求含最多给定点的直线 SJTU OJ 1350 穿越沙漠

    Description

    塞尔达公主又又又又被抓走了。林克为了找到她需要穿过拉纳鲁沙漠,坏消息是林克可能没有足够的体力穿越沙漠,好消息是沙漠中分布着N个力之果实,坏消息是我们的林克只能走直线。为了穿越沙漠,林克希望能够吃到尽可能多的力之果实。现在请你帮他规划一条直线,使他能够获得尽可能多的力之果实。

    Input Format

    输入第一行有一个数N,表示沙漠中果实的数量。

    接下来的N行每行两个正整数x,y,表示每个力之果实的坐标。

    Output Format

    输出一个数,表示林克最多能够获得的力之果实的数量。

    Sample Input

    5
    4 4
    2 2
    9 9
    9 11
    17 8
    

    Sample Output

    3
    

    说明

    对于70%的数据,N<=300

    对于100%的数据,N<=700

    第一种方法: O(n^3)

    任取两个点,i j 作为一条直线, 对剩下的所有点中遍历是否在这条直线上. 维护最大值即可,

    几点注意的地方:

    1.三重循环的起始和结束: 因为i j 和 j i 组成的是同一条直线 所以可以避免重复 . 而k也同样  如果在前面出现过 那么一点计算过 所以不用考虑

    或者换一种思路 认为 i是要进行判断的点, j 和 k组成的是直线,所以由刚才的思路 也可以减少循环量.

    或者再换一种思路, 认为每个在同一条直线上的所有点组成了给定点集的一个子集, 这个子集至少有两个不重复点, 剩下每次加入一给点都可以认为它是第三个点,所以也要不重复,并且关于三点共线的公式可以直接套用外积公式. 

    2.能用乘法不要用除法. 这个算是常识吧..好好的整数运算就可以解决的问题搞一个浮点数出来多傻呀..不仅慢 还 占空间.

    代码如下:

    /*
    Judging... PROB=1350 LANG=C++
    
    Accepted (Time: 6ms, Memory: 4968kb)
    Accepted (Time: 6ms, Memory: 4964kb)
    Accepted (Time: 5ms, Memory: 4956kb)
    Accepted (Time: 6ms, Memory: 4952kb)
    Accepted (Time: 8ms, Memory: 4968kb)
    Accepted (Time: 11ms, Memory: 4956kb)
    Accepted (Time: 17ms, Memory: 4964kb)
    Accepted (Time: 140ms, Memory: 4960kb)
    Accepted (Time: 262ms, Memory: 4968kb)
    Accepted (Time: 187ms, Memory: 4952kb)
    */
    #include <iostream>
    using namespace std;
     
    int x[701]={0},y[701]={0};//700个点
     
    int main(int argc, char const *argv[])
    {
        int n; cin>>n;
        for (int i = 0; i < n; ++i)
        {
            cin>>x[i]>>y[i];
        }
        //Cn2 枚举两个点 确定一条直线
        //然后 枚举每个点 判断三点是否共线
        int max  = 2;
        for (int i = 0; i < n-2; ++i)
        {
            for (int j = i+1; j < n-1; ++j)
            {
                int count = 2;
                //i 和 j 组成了一条直线
                if(x[i]==x[j] and y[i]==y[j])
                    continue;
                for (int k = j+1; k < n; ++k)
                {
                    //研究k点是否在 i j 直线上
                    bool isOn = ( (y[j]-y[i])*(x[k]-x[i]) == (x[j]-x[i]) *(y[k]-y[i]) );
                    if(isOn)
                        count++;
                }
                if(count > max)
                    max = count;
            }
        }
        cout<<max<<endl;
        return 0;
    }
    O(n^3)

    第二种方法稍微优化了一点.其实也就是O(n^2lgn)和O(n^3)的区别, 但是效果也是很显著的.(可以对比一下耗时)

    思想就是,先找到一个点i,算出其他点和这个点的斜率,然后从这个斜率集合中找到重复出现次数最多的次数,这个次数表名了有多少个点在i的同一延伸方向上,在加上i本身,那么就能得到"在某一条含有i的直线与给定点集的交集的大小". 再从这些值中找到最大的 那就是答案.

    这个的优化本质是: 在第一种方法里,每次取三个点进行判断时,其实重复计算了某些斜率 比如当 i j k 和 i k n 或者j k m 时  i k 和 j k的斜率都是重复计算的,

    而我们只要根据i,j 给定的i 任意的j 算出斜率 取出频率最高的那个斜率 就找到了一条待定直线.

    具体代码如下:

    /*
    Judging... PROB=1350 LANG=C++
    
    Accepted (Time: 7ms, Memory: 4968kb)
    Accepted (Time: 8ms, Memory: 4956kb)
    Accepted (Time: 5ms, Memory: 4948kb)
    Accepted (Time: 6ms, Memory: 4960kb)
    Accepted (Time: 7ms, Memory: 4988kb)
    Accepted (Time: 9ms, Memory: 4976kb)
    Accepted (Time: 12ms, Memory: 4980kb)
    Accepted (Time: 40ms, Memory: 4984kb)
    Accepted (Time: 50ms, Memory: 4996kb)
    Accepted (Time: 44ms, Memory: 4988kb)
    */
    
    #include <iostream>
    #include <algorithm>
    #include <cmath>
    using namespace std;
     
    int x[701]={0},y[701]={0};//700个点
     
     
    struct fraction
    {
        int son;
        int mom;
        bool operator == (const fraction& b)const
        {
            return (son*b.mom == b.son*mom);
        }
        bool operator != (const fraction &b)const
        {
            return 1-(*this==b);
        }
    };
    fraction scopes[700];
    int freqs[700]={0};
     
    int cmp_fraction(const void* _a, const void* _b){
        fraction* a = (fraction*) _a;
        fraction* b = (fraction*) _b;
        return (*a).son * (*b).mom - (*b).son * (*a).mom;
    }
     
     
    int main(int argc, char const *argv[])
    {
        int n; cin>>n;
        for (int i = 0; i < n; ++i)
        {
            cin>>x[i]>>y[i];
        }
        //针对每个固定点 枚举后继点 确定斜率
        
        int max  = 2;
        int freqs_len = 0;
        for (int i = 0; i < n-1; ++i)
        {
            //确定经过i点的所有直线的斜率集合 根据这个集合里重复数的个数的最多 即是 含有i点的子集中最多的数量
            //每次的scopes 集合都要更新 只需记录长度即可
            int len = 0;
            int same = 0;
            for (int j = i+1; j < n; ++j)
            {
                if(x[i]==x[j] and y[i]==y[j]){
                    same++;
                    continue;
                }
                fraction k;
                k.son = abs(y[j]-y[i]);//不加不行 - -
                k.mom = abs(x[j]-x[i]);
                scopes[len++] = k;
            }
            //排序 O(nlgn)
            qsort(scopes,len,sizeof(fraction),cmp_fraction);
            //计算频率最多 O(n)
            int maxFreq = 1;
            int freq = 1;
            for (int j = 1; j < len; ++j)
            {
                if(scopes[j]==scopes[j-1])
                    freq++;
                else{//断了
                    if(freq>maxFreq)
                        maxFreq = freq;
                    freq = 1;
                }
            }
            if(freq>maxFreq)
                maxFreq = freq;
            freqs[freqs_len++] = maxFreq + 1 + same;//加上i自己
        }
        for (int i = 0; i < freqs_len; ++i)
        {
            if(max < freqs[i])
                max = freqs[i];
        }
        cout<<max<<endl;
        return 0;
    }
    O(n^2lgn)

    此处用到了结构体来表示分数,而不用浮点数,是因为浮点数计算和判断浮点数相等的过程比较麻烦(虽然代码没这个麻烦,但是计算机计算的时候应该会比这个麻烦)注意qsort函数的使用,

    另外有几个细节是

    1.存储时一定要统一用abs来存储,否则进行排序时会有问题,

    2.用same变量来保存相同的点 这个在第一种方法里也要考虑到

  • 相关阅读:
    【codeforces 755A】PolandBall and Hypothesis
    【codeforces 755B】PolandBall and Game
    【codeforces 755C】PolandBall and Forest
    Enhancing network controls in mandatory access control computing environments
    NPM 使用介绍
    【oracle】首次启动SQL Developer配置java.exe出错(Could not find jvm.cfg! )
    day70-oracle PLSQL_02光标
    day69-oracle 22-DBCA
    day70-oracle 12-Java调用存储过程和存储函数
    day17 16.关于sql注入与PreparedStatement介绍
  • 原文地址:https://www.cnblogs.com/yuchenlin/p/sjtu_oj_1350.html
Copyright © 2011-2022 走看看