• LA3905流星


    题意:
          在一个二维平面上有n个流星,每个流星有自己的初始位置和速度,有一个照相机,张相机的可视范围是一个矩形框,左下角(0,0)右上角(w ,h),然后问你相机的矩形内出现的最多的流星数是多少? 


    思路:
          感觉是一道很不错的题目,想到流星数目,第一反应就是可以把他转化成时间段,我们求出每个流星的进入相机时间,和出相机时间,这样就会得到一个时间段,流星的最大
    数量也就是时间段的最大重叠部分,这个不解释了应该不难理解,现在问题就来了,我们怎么求得所有的时间段呢?一开始我也感觉很麻烦,然后在白书上学了一个比较方便的方法,我们求时间段可以先把流星分解了,x,y单独算,进入的时间肯定是进x进y的最大值,出去的时间肯定是出x,出y的最小值,这样我们分别求完之后就ok了,如果L>=R就证明没有在相机范围内出现过,下面给出求L,R的代码
    void Update(int x ,int a ,int w ,double& L ,double& R)
    {
       if(a == 0)
       {
           if(x <= 0 || x >= w) R = L - 1; //永远也进步了相机
       }
       else if(a > 0)//往上跑 
       {
          L = Max(L ,-(double)x / a);
          R = Min(R ,(double)(w - x) / a);
       }
       else//往下跑
       {
          L = Max(L ,(double)(w - x) / a);//理解不了的注意这里的a是负的
          R = Min(R ,-(double)x / a); 
       }
               
    }


    求x,y的时候都是用的上面的那个,只不过传的参数不一样罢了,具体细节可以看代码。
      这样我们就得到了所有流星的时间段L,R(L>=R的是不满足的,直接丢弃),然后就是求区间的最大重叠面积了,这个也比较好求,如果你不嫌麻烦可以写线段树贪心去求,时间复杂度是O(n*log(n))的,不过有一个更省事更快的方法,就是扫面线法O(n)<其实这么说有漏洞,因为扫描线涉及到排序,排序是O(n*log(n))的,这个地方大家知道就行了>,我们可以把所有端点都扔进结构体数组里,然后按照时间从小到大排序,如果时间相等那么就后端点在前面(这样的原因是题目中说在相机边框上的不算,如果算的话就入端点在前面),排序之后直接扫一遍,遇到前端点就++,后端点就--,过程中最大的值就是答案,这个应该很好理解,不理解的在纸上画一画,每次变化的时候都是在端点上变化的,这个就是经典的扫描线想法,扫描线也可以配合着线段树应用,求重叠面积,体积,周长啥的。这个题目还有一个小小的优化(白书上说的),我们可以躲开浮点运算,因为整个程序里涉及到的除法就是除以速度,速度的范围是绝对值<=10,那么我们直接把被除数扩大1,2,3..10的最小公倍数2520倍就行了,这样保证都是整除,至于为什么我想不用我解释了。






    #include<stdio.h>
    #include<string.h>
    #include<algorithm>


    #define N 100000 + 10


    using namespace std;




    typedef struct
    {
        int mk;
        double time;
    }NODE;


    NODE node[N+N];


    bool camp(NODE a ,NODE b)
    {
         return a.time < b.time || a.time == b.time && a.mk < b.mk;
    }


    double Max(double x ,double y)
    {
        return x > y ? x : y;
    }


    double Min(double x ,double y)
    {
        return x < y ? x : y;
    }


    void Update(int x ,int a ,int w ,double& L ,double& R)
    {
       if(a == 0)
       {
           if(x <= 0 || x >= w) R = L - 1;
       }
       else if(a > 0) 
       {
          L = Max(L ,-(double)x / a);
          R = Min(R ,(double)(w - x) / a);
       }
       else
       {
          L = Max(L ,(double)(w - x) / a);
          R = Min(R ,-(double)x / a); 
       }
               
    }


    int main ()
    {
        int t ,w ,h ,n ,i;
        int x ,y ,a ,b;
        scanf("%d" ,&t);
        while(t--)
        {
           scanf("%d %d" ,&w ,&h);
           scanf("%d" ,&n);
           int nowid = 0;
           for(i = 1 ;i <= n ;i ++)
           {
              scanf("%d %d %d %d" ,&x ,&y ,&a ,&b);
              double L = 0 ,R = 999999999;
              Update(x ,a ,w ,L ,R);
              Update(y ,b ,h ,L ,R);
              if(L < R)
              {
                 node[++nowid].time = L;
                 node[nowid].mk = 1;
                 node[++nowid].time = R;
                 node[nowid].mk = -1;
              }
           }
           int Ans = 0;
           sort(node + 1 ,node + nowid + 1 ,camp);
           int sum = 0;
           for(i = 1 ;i <= nowid ;i ++)
           {
              sum += node[i].mk;
              if(Ans < sum) Ans = sum;
           }
           printf("%d " ,Ans);
        }
        return 0;  
    }
       




    //排除浮点型运算
    void Update(int x ,int a ,int w ,int& L ,int& R)
    {
       if(a == 0)
       {
           if(x <= 0 || x >= w) R = L - 1;
       }
       else if(a > 0) 
       {
          L = Max(L ,-x * 2520 / a);
          R = Min(R ,(w - x) * 2520 / a);
       }
       else
       {
          L = Max(L ,(w - x) * 2520/ a);
          R = Min(R ,-x * 2520/ a); 
       }
               
    }





  • 相关阅读:
    vba根据部门分别汇总不同部门下的人员不同培训内容的时长总计,多条件求和
    vb 案例学习
    bat批处理如何删除本地策略里的用户权限分配中的拒绝从网络访问本机项的guest用户?
    vb,wps,excel 提取括号的数字
    vb,wps,excel 分裂
    vba,excel,网址提取名字与链接url
    母亲节到了 ,送什么礼物好,按键音乐提示,键盘测试,新手电脑
    MySql 数据表从1开始计数
    关于缓存的几点问题
    订单路由定时任务
  • 原文地址:https://www.cnblogs.com/csnd/p/12062640.html
Copyright © 2020-2023  润新知