9-20队内练习赛 G题 Convex Hull of Lattice Points (来自HDU 3285原题)

题目大意:赤裸裸的凸包,有几处变动:基点为最上左的点、顺时针输出凸包顶点(本题最关键处理点:在凸包边上的不输出,只按顺时针 输出顶点)、整形坐标

思路:关键点是把在凸包边上的点弹出,所以有以下几个关键步骤:极坐标排序的时候:共线时选最短距离的点优先;top从1开始,所以当然while中top要验证是正数

下面举一个9个点的例子:

9-20队内练习赛 G题 Convex Hull of Lattice Points (来自HDU 3285原题)_第1张图片

开始之前先说明:图中的点排序后我就用字典序表明顺序了

在这着重讲以下开始和最后:(适合基础入门,大牛请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<iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h> 
#include<math.h>
 
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<b.y;重大修改:下左点改为上左点
    if(a.y!=b.y) return a.y>b.y;
    if(a.y==b.y&&a.x!=b.x) return a.x<b.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)<Dist(b,temp);
 
}
 
 
 
int main()
 
{   int n,k,len;
 
    cin>>k;
 
    
    int cas;
    while(k--)
 
    {

      cin>>cas>>n;
 
     for(int i=0;i<n;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<n;i++)//i=3重大修改 :好把共线的点弹出去
 
      { 

            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  "<<i<<" "<< p[i].x<<" "<<p[i].y<<endl;
 
      }

      
      cout<<cas<<' '<<top+1<<endl;
      for(int i=0;i<=top;i++)
 
         cout<<res[i].x<<" "<<res[i].y<<endl;
 
    }

    

    system("pause");
 
    return 0;
 
}

 

你可能感兴趣的:(c,struct,System,ini)