[算法竞赛入门经典UVa1606]两亲性分子
程序员文章站
2022-03-02 10:21:42
...
[算法竞赛入门经典UVa1606]两亲性分子
题目大意:
平面上有n()个点,每个点为白点或者黑点。现在需要放置一条隔板,使得隔板一侧的白点数加上另一侧的黑点数总数最大。隔板上的点可以看作是任意一侧。
这个题的思路是先枚举基准点A,再以基准点A为原点,算出其他点相对基准点的位置,以基准点为原点建立坐标;枚举其它点中的一个B,让基准点A和点B连成一条线作为本题的隔板,统计隔板两边黑点和白点的个数,更新答案。
解题方法:
白点黑点的处理:
把黑点旋转180°,然后统计时只统计隔板一侧的点数
判断点在隔板的一侧:首先把所有其他点按照极角增序排序,利用叉积来判断两个点之间的夹角,如果大于0那么在隔板左侧,等于0和隔板共线
极角:
利用atctan函数
c++中有两个计算极角的函数:atan(反正切函数)和atan2(四象限反正切函数)
atan的取值范围为 atan2的取值范围为
atan2函数可以区分点所在的象限,更加准确
叉积:
扫描时计算L和R的叉积如果,那么新点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;
}
参考: