题目大意
一种奇怪的虫子不能右转且走过路线之间不能有交点,吃植物才能存活,给出植物的坐标,求虫子要怎样走才能活得最久(吃的植物越多活越久)
输入:样例数,n组样例,每组给出一个n,然后n行每行给出3个数,分别是植物编号、植物x坐标、植物y坐标
输出:能吃的最大植物数目,并给出路线
解体思路
因为虫子只能左转且路线不能有交点,很容易想到让虫子逆时针螺旋地去吃植物,由外到内,可以将所有植物吃完。因为逆时针路线一定是左转,螺旋线保证不相交,从最外到最内是肯定可以吃完所有植物的。
怎样构造这样的螺旋线呢?这要用极角排序。从最下方的点开始,将这个点作为基点,对其他所有点进行极角排序,然后取极角最小点,重新设立为基点,对剩余点重新排序,依次类推直至剩最后一个点。
为什么要从最下点开始?因为这样可以保证其他点都在上方,这样就可以使其他点与该点的极角都>=0,这样可以以x轴正方向为基线找出夹角最小的点。之后由找到的新点为基点,可以保证其他未选择的点都在上一个点与新基点连线的一侧,这样就可以以这条连线作为基线求最小极角。
怎样求对极角进行排序?简单的做法是用sort函数,然后自定义一个cmp比较函数,大致如下:
//极角排序规则
bool cmp(const Point &p1, const Point &p2)
{
double tmp;
tmp=multiply(p1,p2,PointSet[cnt]);
//外积大于0
if(tmp>0)
{
return true;
}
//两线重合时选择距离更小的
else if(fabs(tmp)<=eps && dis(PointSet[cnt],p1)<dis(PointSet[cnt],p2))
{
return true;
}
return false;
}
这里没有直接求角度,而是利用外积来判断,外积函数如下:
//小于0,说明向量p0p1的极角大于p0p2的极角
double multiply(Point p1,Point p2,Point p0)
{
return((p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y));
}
显然,若p1p0与p2p0的外积>0,则p1在p2右侧(若以上述的基线为水平线),相反,若外积<0,则p1在p2左侧,而我们要的极角最小的点即在最右侧的点。若外积为0,则三点共线,我们需要优先选择更靠近基点的点。
AC代码
#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define eps 0.00000001
using namespace std;
struct Point
{
double x,y,num;
};
const int maxN=55;
Point PointSet[maxN]; //输入的点集
Point ans[maxN]; //输出的点集
int n; //点的个数
int cnt; //当前判断基点
//小于0,说明向量p0p1的极角大于p0p2的极角
double multiply(Point p1,Point p2,Point p0)
{
return((p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y));
}
//p1p2距离
double dis(Point p1,Point p2)
{
return(sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y)));
}
//极角排序规则
bool cmp(const Point &p1, const Point &p2)
{
double tmp;
tmp=multiply(p1,p2,PointSet[cnt]);
//外积大于0
if(tmp>0)
{
return true;
}
//两线重合时选择距离更小的
else if(fabs(tmp)<=eps && dis(PointSet[cnt],p1)<dis(PointSet[cnt],p2))
{
return true;
}
return false;
}
int main()
{
int cas;
scanf("%d",&cas);
while(cas--)
{
scanf("%d",&n);
memset(PointSet,0,sizeof PointSet);
int i;
for(i=0;i<n;i++)
{
scanf("%d%lf%lf",&PointSet[i].num,&PointSet[i].x,&PointSet[i].y);
//找出最下点
if(PointSet[i].y<PointSet[0].y)
{
Point tmp=PointSet[0];
PointSet[0]=PointSet[i];
PointSet[i]=tmp;
}
}
//初始基点
cnt=0;
//排序
sort(PointSet+1,PointSet+n,cmp);
ans[cnt]=PointSet[cnt++];
for(i=2;i<n;i++)
{
//依次设立基点排序
sort(PointSet+cnt,PointSet+n,cmp);
ans[cnt]=PointSet[cnt++];
}
ans[cnt]=PointSet[cnt++];
printf("%d",cnt);
for(i=0;i<cnt;i++)
{
printf(" %d",ans[i].num);
}
printf("\n");
}
return 0;
}