• 2019年华南理工大学程序设计竞赛(春季赛) 单身狗救星 (凸包+二分)


    https://ac.nowcoder.com/acm/contest/625/J

    题目:有n个男的 , m 个女的 ,女生去选择男生为好丽友 , 选择的好丽友的价值为 (y1-y2) / (x1-x2) ; 

    问女生选了哪些男生 , 如果有多个男生价值相同选择编号最小的

    分析:看到价值 是(y1-y2) / (x1-x2) 的形式 , 多么容易想到是凸包哦 , (其实有考虑过斜率DP,太菜了不会)

    既然女生是去匹配男生的 , 那很容易啊 , 直接对男生建里凸包 , 然后让女生在男生的凸包里面去找到答案 , 因为我们知道凸包其实是一种最优的状态 ;但是!!!这其实是错误的 , 如果女的点没有在凸包里面这样思考就是正确的 , 但是女生是有可能在凸包里面的,所以不能建立凸包后在去配对 , 而是应该一边建立凸包一边找答案 , 为什么?  这是因为凸包有局部的最优状态 , 如果我们把所有的点对x排序 , 如果扫描到是男生就建立凸包 , 是女生就在凸包找答案 , 因为对女生来说如果想要在左边的男生里面找到最优的,肯定是在凸包里面找答案,为了加快我们可以二分找答案好吧  。 那问题来了 , 我们只是解决了女生在左边找男生 , 但是女的最优男生也有可能是在右边啊?  这时候我们就像找左边一样 , 从右到左建凸包 ,然后找答案

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define PI 3.1415926535
    using namespace std;
    #define ll long long
    const int maxn=1e5+1;
    struct node
    {
        ll x,y;
        int id;
        int now;
    };
    node vex[maxn<<1];//存入的所有的点
    node stackk[maxn],a[maxn],b[maxn];//凸包中所有的点
    int ans[maxn];
    bool cmp1(node a,node b)//排序找第一个点
    {
        if(a.x==b.x)
            return a.y<b.y;
        else
            return a.x<b.x;
    }
    
    ll cross(node a,node b,node c)//计算叉积
    {//向量ab在向量ac
    //>0 顺时针方向
    //<0 逆时针方向
    //=0 平行
        return (b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y);
    }
     
    bool judge(node a , node b , node c)
    {
        ll t=cross(a,b,c);
        return t==0?c.now<b.now : t<0;///相同的价值取编号最小
    }
     
    void solve(int t)
    {
        int top=0;
        for(int i=1 ; i<=t ; i++)
        {
           if(vex[i].id==1)///男生就建立凸包
           {
               while( top >1&&  cross(stackk[top-1] , stackk[top] , vex[i])<0)
                top--;
               stackk[++top]=vex[i];
     
              // printf("%d
    ",top);
           }
           else///*女生就在已有的男生凸包里面找答案
           {
               if(!top) continue;
               int head=1 , tail=top;
               while(head<tail)
               {
                   int mid = (head + tail) >>1;
                 //  ll T=cross(stackk[mid],stackk[mid+1],vex[i]);
                  // cout<<T<<endl;
                   if(cross(stackk[mid],stackk[mid+1],vex[i]) <= 0)  ///可以理解为从mid 断开 的凸包与女生是否可以了建立背包
                    tail=mid;
                   else
                    head=mid+1;
     
               }
     
     
               if(ans[vex[i].now]==-1 || judge(b[vex[i].now] , a[ans[vex[i].now]] , a[stackk[head].now]))
               {
                  ans[vex[i].now] = stackk[head].now;
     
               }
           }
        }
    }
    int main()
    {
            int n,m;
            int t;
            scanf("%d%d",&n,&m);
            t=n+m;
            for(int i=1; i<=n; i++)
            {
                scanf("%lld%lld",&vex[i].x,&vex[i].y);
                vex[i].id=1;
                vex[i].now=i;
                a[i]=vex[i];///记录男的
     
            }
            for(int i=1 ; i<=m ; i++)
            {
                scanf("%lld%lld",&vex[i+n].x,&vex[i+n].y);
                vex[i+n].id=2;
                vex[i+n].now=i;
                b[i]=vex[i+n];///记录女的
            }
     
                memset(stackk,0,sizeof(stackk));
                sort(vex+1,vex+t+1,cmp1);///对x排序跑一遍凸包
                memset(ans,-1,sizeof(ans));
                solve(t);
                reverse(vex+1 , vex+1+t);///从右往左求ub
                for(int i=1 ; i<=t ; i++)
                {
                    vex[i].x=-vex[i].x;
                    vex[i].y=-vex[i].y;
                }
                solve(t);
               for(int i=1 ; i<=m ; i++)
                    printf("%d
    ",ans[i]);
    }
    View Code
  • 相关阅读:
    vue 自定义全局按键修饰符
    Vue 过滤器
    v-if、v-show 指令
    其他内置函数
    python中序列化和反序列化
    jmeter图形化html报告核心指标介绍
    jmeter在linux系统下如何进行压力测试
    文件操作的其他方法
    文件处理操作
    内置函数reduce()
  • 原文地址:https://www.cnblogs.com/shuaihui520/p/10718415.html
Copyright © 2020-2023  润新知