欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

[算法竞赛入门经典UVa1606]两亲性分子

程序员文章站 2022-03-02 10:21:42
...

[算法竞赛入门经典UVa1606]两亲性分子

题目

题目大意:

平面上有n(n1000n \le 1000)个点,每个点为白点或者黑点。现在需要放置一条隔板,使得隔板一侧的白点数加上另一侧的黑点数总数最大。隔板上的点可以看作是任意一侧。

这个题的思路是先枚举基准点A,再以基准点A为原点,算出其他点相对基准点的位置,以基准点为原点建立坐标;枚举其它点中的一个B,让基准点A和点B连成一条线作为本题的隔板,统计隔板两边黑点和白点的个数,更新答案。

解题方法:

白点黑点的处理:

把黑点旋转180°,然后统计时只统计隔板一侧的点数

判断点在隔板的一侧:首先把所有其他点按照极角增序排序,利用叉积来判断两个点之间的夹角,如果大于0那么在隔板左侧,等于0和隔板共线

极角:

利用atctan函数

c++中有两个计算极角的函数:atan(反正切函数)和atan2(四象限反正切函数)

atan的取值范围为(π2,π2)(-\frac{\pi}{2},\frac{\pi}{2}) atan2的取值范围为(π,π)(-{\pi},{\pi})

atan2函数可以区分点所在的象限,更加准确

叉积:
u×v=(u1v2u2v1)n=(uvsinθ)n\vec u \times \vec v=(u_1v_2-u_2v_1) \vec n = ( \vert \vec u \vert \vert \vec v \vert sin \theta ) \vec n
(1)u1v2u2v1>0,sinθ>0,θ(0,π),vnπ(1)当u_1v_2-u_2v_1 > 0,sin\theta >0 ,\theta \in (0,\pi) , \vec v相对 \vec n逆时针旋转 \pi以内
(2)u1v2u2v1<0,sinθ<0,θ(π,2π),vnπ(2)当u_1v_2-u_2v_1 < 0,sin\theta <0 ,\theta \in (\pi,2\pi) , \vec v相对 \vec n逆时针旋转 \pi以上
(3)u1v2u2v1=0,sinθ=0,vn线(3)u_1v_2-u_2v_1 = 0,sin\theta = 0 , \vec v 与 \vec n共线

扫描时计算L和R的叉积如果0\ge0,那么新点R在隔板L的左侧,符合要求

#include<cmath>
#include<cstdio>
#include<algorithm>
#define rep(i,a,b) for(int i=(a); i<(int)(b);++i)
const int maxn=1005;
int n,color[maxn];

struct node
{
    int x,y;
    double rad;
    bool operator < (const node& rhs)const
    {
        return rad<rhs.rad;
    }
}op[maxn],p[maxn];

bool left(node& A,node& B)//叉积
{
    return A.x*B.y-A.y*B.x>=0;
}

int infer()
{
    if(n<=2)
        return 2;
    int ans=0;
    rep(i,0,n)
    {
        int k=0;
        rep(j,0,n)
            if(i!=j)
            {
                p[k].x=op[j].x-op[i].x;
                p[k].y=op[j].y-op[i].y;
                if(color[j])//把黑点坐标旋转180,并且统计时只统计一边
                {
                    p[k].x=-p[k].x;
                    p[k].y=-p[k].y;
                }
                p[k].rad=atan2(p[k].y,p[k].x);
                ++k;
            }
        std::sort(p,p+k);
        int L=0,R=0,cnt=2;//开始有两个点,一个是i,也就是算完相对位置后的原点,一个是L
        while(L<k)//把隔板L转圈,一直转完所有的点
        {
            if(L==R)//???
            {
                R=(R+1)%k;//R为[0,k)
                ++cnt;
            }
            while(R!=L&&left(p[L],p[R]))//R是L左边的点,右手定则
            {
                R=(R+1)%k;
                ++cnt;
            }
            --cnt;//舍去多计入的点,也可以理解为由于分隔线的旋转,原来在分隔线上的点现在变为了右侧的点,要减掉一个
            ++L;
            ans=std::max(ans,cnt);
        }
    }
    return ans;
}

int main()
{
    while(scanf("%d",&n)==1&&n)
    {
        rep(i,0,n)
            scanf("%d%d%d",&op[i].x,&op[i].y,&color[i]);
        printf("%d\n",infer());
    }
    return 0;
}

参考:

atan2与极坐标

叉积

相关标签: 几何