题目大意:赤裸裸的凸包,有几处变动:基点为最上左的点、顺时针输出凸包顶点(本题最关键处理点:在凸包边上的不输出,只按顺时针 输出顶点)、整形坐标
思路:关键点是把在凸包边上的点弹出,所以有以下几个关键步骤:极坐标排序的时候:共线时选最短距离的点优先;top从1开始,所以当然while中top要验证是正数
下面举一个9个点的例子:
开始之前先说明:图中的点排序后我就用字典序表明顺序了
在这着重讲以下开始和最后:(适合基础入门,大牛请PASS~-)开始:B点利马被弹了。
最后:由C点把E、D都弹了,然后来到关键的G点,注意此时的G点是先找来H点而不是J点,因为之前我们是在更新的时候选择最短的 优先,所以尽管H因为HG是GC的右拐而进栈,但是很快就因为IH是HG的左拐而被弹了,I点因为IG是GC右拐进栈,但是很快有因为JI是IC的左拐而被弹,然后判断JG 与GC的位置关系,此时JG因为和GC共线,所以G点也被弹了,最后一次判断:JC是AC的左拐,符合条件,进栈。至此,循环结束。
所以最后HIJ这三个点被弹的顺序是:蓝红黄的顺序
program:://以下程序因为是从我的模板直接改的,所以会有几处注明变动
#include
#include
#include
#include
#include
using namespace std;
struct point
{
int x,y;
};
point p[105],res[105];
double Dist(const point &arg1, const point &arg2)
{
return sqrt((double) ((arg1.x - arg2.x)*(arg1.x - arg2.x) + (arg1.y - arg2.y)*(arg1.y - arg2.y)) );
}
int multi(int i,int top,point p2,point p1,point p0)//传i和top只是为了便于调试,不要也行
{
return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y);
}
int mysort1(point a,point b)
{ //if(a.y!=b.y) return a.y
if(a.y==b.y&&a.x!=b.x) return a.x
}
bool cmp(const point &a,const point &b)
{
point temp=p[0];
double xmt=(a.x-temp.x)*(b.y-temp.y)-(b.x-temp.x)*(a.y-temp.y);
if(xmt)
return xmt<0;
//return xmt>0;重大修改:逆时针改为顺时针
//return Dist(a,temp)>Dist(b,temp);//向量共线取最长的改为取最短的。
return Dist(a,temp)
}
int main()
{ int n,k,len;
cin>>k;
int cas;
while(k--)
{
cin>>cas>>n;
for(int i=0;i
cin>>p[i].x>>p[i].y;
sort(p,p+n,mysort1);//排序,找到最左下角的点
res[0]=p[0];
sort(p+1,p+n,cmp);//按照极角排序
res[1]=p[1];
//res[2]=p[2];重大修改:好把共线的点弹出去
int top=1;//重大修改:好把共线的点弹出去
for(int i=2;i
{
while(top&&multi(i,top,p[i],res[top],res[top-1])>=0)
//重大修改:向左拐弹出改为向右拐弹出
top--;
res[++top]=p[i];
//其实这里的上面的cmp选择距离短的优先是有很大好处的,因为可以在开始的
//时候就可以把与原点共线的(向左拐的当然会弹出了)都弹出了
//弹出的原理是:每次退到原点都停止退栈,此时直接选择下一个点进栈
//因为当前的这个点+前面的点+原点不是共线就是向左拐了,所以
//直接就可以舍弃当前此点,所以无论开始的时候有几个与原点共线的点
//都会因为每次退回原点而被迫选这下一点,就是进一次就退一次
//进一次退一次,直到找到右拐的点。
//至于最后的共线的点,嘿嘿,选择短距离优先的好处就是体现在这里
//直接因为右拐而把最后的点都弹了
//cout<<"i p[i].x p[i].y "<
}
cout<
cout<
}
system("pause");
return 0;
}