J-单身狗救星 (下凸包)2019年华南理工大学程序设计竞赛(春季赛)

版权声明:转载注明下出处就行了。 https://blog.csdn.net/LJD201724114126/article/details/89359234

题目链接:哆啦A梦传送门

题意:女生有一个 美丽程度是B1,聪明程度是I1,男生有一个 帅气程度是B2,聪明程度是I2。

现在有n个男生,m个女生,你要给m个女生找到最适合她的男生,一个男生可以被多个女生选择。

女生与男生的适合度为 :

      

化简得 : {\color{Red} \frac{I1-I2}{B1-B2}} ,可以看出其实就是女生点与男生点直接的斜率。

我们先把所有点排好序(先按x排,再按y排),然后我们对这些男生点进行下凸包,当遇到女生点时,我们就在这个女生点之前构造的下凸包二分出一个斜率最大的男生点,为什么要下凸包,而不上凸包,因为当遇到女孩点,每次都是在左边找一个点,使得斜率最大,那他要找的点就应该尽量往下。

上面找的是每个在女孩点的左边给她找了个男孩点,但有可能这个女孩点最优的男孩点不在左边,而在右边,故我们还需再来一次下凸包,这次就从后面开始。

图1:见代码                                                                                           图2:见代码

扫描二维码关注公众号,回复: 5938254 查看本文章

                                           

代码标程参考官方标程。

#include<bits/stdc++.h>

using namespace std;

typedef long long LL;

const int maxn=200010;

struct point{

    LL x,y;///点的坐标
    int id,flag; ///id表示这个点的位置,flag=1表示此点是男孩点,flag=2表示女孩点
    point(){}
    point(LL _x,LL _y,int _id,int _flag){
        x=_x;y=_y;id=_id;flag=_flag;
    }

    bool operator < (const point &b){
    return x==b.x?y<b.y:x<b.x;
    }
}p[maxn],ch[maxn],ans1[maxn],ans2[maxn];

int pos[maxn],tot;

point operator + (point a,point b){ return point(a.x+b.x,a.y+b.y,0,0);}
point operator - (point a,point b) {  return point(a.x-b.x,a.y-b.y,0,0);}




LL cross(point a,point b){  return a.x*b.y-a.y*b.x;}

///前男友 女生 现男友
bool judge(point a,point b,point c){
    LL item=cross(a-b,c-b);

    ///要都适合,选择最小编号(最年轻)的男友
    return item==0?c.id<a.id:item<0;
}

void andrew(int n)
{
    tot=-1;

    for(int i=0;i<n;i++)
    {
        ///遇到男孩点,就下凸包
        if(p[i].flag==1){
        while(tot>0&&cross(ch[tot]-ch[tot-1],p[i]-ch[tot-1])<0)
            tot--;
        ch[++tot]=p[i];
        }
        else{

            if(tot==-1) continue;
            int l=0,r=tot;
            ///二分下凸包,找到最优的点
            while(l<r)
            {
                int mid=(l+r)>>1;
                    ///见图1
                if(cross(ch[mid+1]-ch[mid],p[i]-ch[mid])>0)
                    l=mid+1;
                else r=mid;
            }
                ///如果此女生还没被匹配,直接把这男生给她,要以匹配,就要比较下这两个男生
            if(pos[p[i].id]==-1||judge(ans1[pos[p[i].id]],ans2[p[i].id],ans1[ch[l].id]))
                pos[p[i].id]=ch[l].id;
        }
    }
}

int main()
{

    int n,m;
    LL x1,y1;
    scanf("%d%d",&n,&m);

    for(int i=0;i<n+m;i++)
    {
        scanf("%lld%lld",&x1,&y1);
        if(i<n){
                ///ans1存储男生点
            ans1[i]=point(x1,y1,i,1);
            p[i]=ans1[i];///p存储所有点
        }
        else{
            ///ans2存储女生点
            ans2[i-n]=point(x1,y1,i-n,2);
            p[i]=ans2[i-n];
        }
    }

    memset(pos,-1,sizeof(pos));
    ///将所有点排序
    sort(p,p+n+m);

    ///第一次下凸包,在女孩点的左边找一个最优的男孩点
    andrew(n+m);


    ///第二次下凸包,在女孩点的右边找一个最优的男孩点
    ///翻转,并把坐标都取反
    reverse(p,p+n+m);
    for(int i=0;i<n+m;i++)
    {
        p[i].x=-p[i].x;
        p[i].y=-p[i].y;
    }
    andrew(n+m);

    for(int i=0;i<m;i++)
        printf("%d\n",pos[i]+1);


    return 0;
}

我的标签:做个有情怀的程序员。

猜你喜欢

转载自blog.csdn.net/LJD201724114126/article/details/89359234
今日推荐