注:《黑书》P392中有讲:对于通常求的凸包,输出的凸包是包括所有可能的共线点还是不包括任何共线点(即只有极点)这是取决于题目的题目要求的。在下面的程序中都会将去共线点的代码加上,视题目删除。
1、http://poj.org/problem?id=1113 简单凸包,求凸包的周长和圆周
Graham-Scan 算法
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#include<algorithm>
#include<ctime>
using namespace std;
#define maxn 1005
#define eps 1e-6
#define pi acos(-1.0)
struct point
{
double x,y;
}p[maxn],sta[maxn];
int top;
int Fabs(double d)
{
if(fabs(d)<eps) return 0;
else return d>0?1:-1;
}
double x_multi(point p1,point p2,point p3)
{
return (p2.x-p1.x)*(p3.y-p1.y)-(p3.x-p1.x)*(p2.y-p1.y);
}
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(point a,point b)//按极角排序
{
if(Fabs(x_multi(p[0],a,b))>0) return 1;
if(Fabs(x_multi(p[0],a,b))<0) return 0;
if(Fabs(Dis(p[0],a)-Dis(p[0],b))<0)
return 1;
return 0;
}
void Graham(int n)
{
int i,k=0,tot;
for(i=1;i<n;i++)
if((p[i].y<p[k].y)||((p[i].y==p[k].y)&&(p[i].x<p[k].x)))
k=i;
swap(p[0],p[k]);
sort(p+1,p+n,cmp);
tot=1;
for(i=2;i<n;i++) //去共线点
if (Fabs(x_multi(p[i],p[i-1],p[0])))
p[tot++]=p[i-1];
p[tot++]=p[n-1];
sta[0]=p[0],sta[1]=p[1];
i=top=1;
for(i=2;i<tot;i++)
{
while(top>=1&&Fabs(x_multi(p[i],sta[top],sta[top-1]))>=0)
{
if(top==0) break;
top--;
}
sta[++top]=p[i];
}
}
int main()
{
int n,i;
double r,ans=0.0;
scanf("%d%lf",&n,&r);
for(i=0;i<n;i++)
scanf("%lf%lf",&p[i].x,&p[i].y);
Graham(n);
for(i=0;i<=top;i++)
ans+=Dis(sta[i],sta[(i+1)%(top+1)]);
printf("%.0lf\n",ans+2.0*pi*r);
return 0;
}
2、 http://poj.org/problem?id=3348 简单凸包,求凸包的面积
Graham求凸包+叉积求有向面积
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#include<algorithm>
#include<ctime>
using namespace std;
#define maxn 1005
#define eps 1e-6
#define pi acos(-1.0)
struct point
{
double x,y;
}p[maxn],sta[maxn];
int top;
int Fabs(double d)
{
if(fabs(d)<eps) return 0;
else return d>0?1:-1;
}
double x_multi(point p1,point p2,point p3)
{
return (p2.x-p1.x)*(p3.y-p1.y)-(p3.x-p1.x)*(p2.y-p1.y);
}
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(point a,point b)
{
if(Fabs(x_multi(p[0],a,b))>0) return 1;
if(Fabs(x_multi(p[0],a,b))<0) return 0;
if(Fabs(Dis(p[0],a)-Dis(p[0],b))<0)
return 1;
return 0;
}
void Graham(int n)
{
int i,k=0,tot;
for(i=1;i<n;i++)
if((p[i].y<p[k].y)||((p[i].y==p[k].y)&&(p[i].x<p[k].x)))
k=i;
swap(p[0],p[k]);
sort(p+1,p+n,cmp);
tot=1;
for(i=2;i<n;i++)
if (Fabs(x_multi(p[i],p[i-1],p[0])))
p[tot++]=p[i-1];
p[tot++]=p[n-1];
sta[0]=p[0],sta[1]=p[1];
i=top=1;
for(i=2;i<tot;i++)
{
while(top>=1&&Fabs(x_multi(p[i],sta[top],sta[top-1]))>=0)
{
if(top==0) break;
top--;
}
sta[++top]=p[i];
}
}
int main()
{
int n,i;
double ans=0.0;
scanf("%d",&n);
for(i=0;i<n;i++)
scanf("%lf%lf",&p[i].x,&p[i].y);
Graham(n);
for(i=1;i<top;i++)
ans+=x_multi(sta[0],sta[i],sta[i+1])/2;
printf("%.0lf\n",(floor)(ans/50));
return 0;
}
3、 http://poj.org/problem?id=1228 中等,给出一些已经缺失某些点的点集,判断给定的点是否唯一确定一个凸包。涉及到凸包点的性质,首先题目已经说明了剩余的点肯定构成一个凸包,但要求的是能否用这些点确定原来所以点构成凸包的唯一性。有一个必要条件是:如果剩余所有点构成的凸包每条边上都至少有三个点那么就能确定原来的凸包的唯一性即三点确定凸包的某条边。 证明:1、如果凸包某条边上点的个数有且只有两个,那么如果缺失的点是在这条边的外面,则会构成一个新的凸包,用橡皮筋的拉扯来说明最形象,如果两个钉子之间固定一条橡皮筋,如果在外面还有一个点,那么可以将橡皮筋拉过去构成的仍是一个凸包不过是新的凸包 ;2、如果某条边的点的个数大于三个,那么肯定能确定一个唯一的凸包,同样以橡皮筋举例,三个钉子固定一橡皮筋,如果外面还有一个点的话的确可以将橡皮筋拉过去构成一个新的多边形,但是,不满足题目说的本来就是凸包。
题目讨论区中因此大家也给出一些结论,题目给出的点个数至少为6个,所有点不共线,凸包每条边上至少有三个点即一条边除去极点外还有其他的点。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#include<algorithm>
#include<ctime>
using namespace std;
#define maxn 1005
#define eps 1e-6
#define pi acos(-1.0)
int Fabs(double d)
{
if(fabs(d)<eps) return 0;
else return d>0?1:-1;
}
struct point
{
double x,y;
bool operator == (const point &p)
{
return Fabs(x-p.x)==0&&Fabs(y-p.y)==0;
}
}p[maxn],pp[maxn],sta[maxn];
int top;
double x_multi(point p1,point p2,point p3)
{
return (p2.x-p1.x)*(p3.y-p1.y)-(p3.x-p1.x)*(p2.y-p1.y);
}
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(point a,point b)
{
if(Fabs(x_multi(p[0],a,b))>0) return 1;
if(Fabs(x_multi(p[0],a,b))<0) return 0;
if(Fabs(Dis(p[0],a)-Dis(p[0],b))<0)
return 1;
return 0;
}
bool Onsegment(point p1,point p2,point p3)
{
double min_x=min(p1.x,p2.x);
double min_y=min(p1.y,p2.y);
double max_x=max(p1.x,p2.x);
double max_y=max(p1.y,p2.y);
if(p3.x>=min_x&&p3.x<=max_x&&p3.y>=min_y&&p3.y<=max_y)
return true;
return false;
}
void Graham(int n)
{
int i,k=0,tot;
for(i=1;i<n;i++)
if((p[i].y<p[k].y)||((p[i].y==p[k].y)&&(p[i].x<p[k].x)))
k=i;
swap(p[0],p[k]);
sort(p+1,p+n,cmp);
tot=1;
for(i=2;i<n;i++)
if (Fabs(x_multi(p[i],p[i-1],p[0])))
p[tot++]=p[i-1];
p[tot++]=p[n-1];
sta[0]=p[0],sta[1]=p[1];
i=top=1;
for(i=2;i<tot;i++)
{
while(top>=1&&Fabs(x_multi(p[i],sta[top],sta[top-1]))>=0)
{
if(top==0) break;
top--;
}
sta[++top]=p[i];
}
}
int main()
{
int t,n,i,j;
bool flag;
scanf("%d",&t);
while(t--)
{
scanf("%d%",&n);
for(i=0;i<n;i++)
{
scanf("%lf%lf",&p[i].x,&p[i].y);
pp[i]=p[i];
}
if(n<6)
{
puts("NO");
continue;
}
flag=false;
for(i=2;i<n;i++)//判断所有的点是否共线
if(Fabs(x_multi(p[0],p[1],p[i]))!=0)
{
flag=true;
break;
}
if(!flag)
{
puts("NO");
continue;
}
Graham(n);
for(i=0;i<=top;i++)
{
flag=false;
for(j=0;j<n;j++)//判断每条边上除了极点还有其他的点
if(!(pp[j]==sta[i])&&!(pp[j]==sta[(i+1)%(top+1)]))
if(Onsegment(sta[i],sta[(i+1)%(top+1)],pp[j]))
{
flag=true;
break;
}
if(!flag)
break;
}
printf("%s\n",flag?"YES":"NO");
}
return 0;
}
《黑书》中有以求平面点集最远点对距离,可以利用凸包性质改进最坏情况时间复杂度。本题数据较水,可以Graham后直接暴力,黑书推荐方法就是旋转卡壳。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#include<algorithm>
#include<ctime>
using namespace std;
#define maxn 50005
#define eps 1e-6
#define pi acos(-1.0)
int Fabs(double d)
{
if(fabs(d)<eps) return 0;
else return d>0?1:-1;
}
struct point
{
double x,y;
bool operator == (const point &p)
{
return Fabs(x-p.x)==0&&Fabs(y-p.y)==0;
}
}p[maxn],sta[maxn];
int top;
double x_multi(point p1,point p2,point p3)
{
return (p2.x-p1.x)*(p3.y-p1.y)-(p3.x-p1.x)*(p2.y-p1.y);
}
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(point a,point b)
{
if(Fabs(x_multi(p[0],a,b))>0) return 1;
if(Fabs(x_multi(p[0],a,b))<0) return 0;
if(Fabs(Dis(p[0],a)-Dis(p[0],b))<0)
return 1;
return 0;
}
void Graham(int n)
{
int i,k=0,tot;
for(i=1;i<n;i++)
if((p[i].y<p[k].y)||((p[i].y==p[k].y)&&(p[i].x<p[k].x)))
k=i;
swap(p[0],p[k]);
sort(p+1,p+n,cmp);
tot=1;
for(i=2;i<n;i++)
if (Fabs(x_multi(p[i],p[i-1],p[0])))
p[tot++]=p[i-1];
p[tot++]=p[n-1];
sta[0]=p[0],sta[1]=p[1];
i=top=1;
for(i=2;i<tot;i++)
{
while(top>=1&&Fabs(x_multi(p[i],sta[top],sta[top-1]))>=0)
{
if(top==0) break;
top--;
}
sta[++top]=p[i];
}
}
int main()
{
int n,i,j;
while(~scanf("%d",&n))
{
for(i=0;i<n;i++)
scanf("%lf%lf",&p[i].x,&p[i].y);
double ans=0.0,tmp;
Graham(n);
for(i=0;i<=top;i++)
for(j=i+1;j<=top;j++)
{
tmp=Dis(sta[i],sta[j]);
if(tmp>ans) ans=tmp;
}
/*旋转卡壳*/
/*sta[top+1]=sta[0];
for(i=0,j=1;i<=top;i++)
{
while(x_multi(sta[i],sta[i+1],sta[j+1])>x_multi(sta[i],sta[i+1],sta[j]))
j=(j+1)%(top+1);
ans=max(ans,max(Dis(sta[i+1],sta[j+1]),Dis(sta[i],sta[j])));
}*/
printf("%.0lf\n",ans*ans);
}
return 0;
}