今天开始计算几何之旅。
第一章讲的是凸包,之前接触过,想就草草开始下一章,但是想到要与队友共勉,还是认真的看了,来总结一下。
------------------------------------------------
凸包问题大致上就是求解一个点集的最外围点。图就不上了。了解凸包的模型,用模板一般就可以了。自己敲了个模板下文中会贴出来。A了hdu的两题凸包,验证了正确性。
暴力的做法O(n^3)。这里介绍一种O(n*log(n))的方法。
首先对点进行排序。x小的在前面,当x一样大小时,y小的在前面。这样我们就得到了最左边的点和最右边的点。
通过这两个点我们把凸包分成两部分。上半部分和下半部分。
你可以把凸包想象成一个圆,现在我们把圆分成了上下两个半圆。
现在我们有点集中的3个点。且3个点都是有序的如果点2在点1,3所成直线的下方。那么点2必然不属于上凸包。
如此。我们每次都判断从左到右的最后3个点是否符合要求如若不然删除中间的点既点2;这样一直到所有点都遍历了,我们就得到了上凸包;
同理可得下凸包
再介绍一下叉积,高等数学应该学过。他可以判断点在直线的上方还是下方。
如果纯文字看不明白,结合一下代码
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<map>
#include<set>
#include<stack>
#include<queue>
#include<cmath>
#include<iostream>
using namespace std;
const int MAX = 1e6+5;
const double pi = 3.1415926;
typedef int element;
struct Point{
element x , y ;
friend bool operator < ( Point p1 , Point p2 ){
return p1.x<p2.x || ( p1.x==p2.x && p1.y > p2.y );
}
};
double dist( Point p1 , Point p2 ){
return sqrt( (double)(p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y) );
}
struct Tubao{
Point lower[MAX] , upper[MAX];//分别保存上凸包和下凸包的点。
Point allPoint[MAX];
int Size;
int topLower , topUpper;
void init(){
sort(allPoint,allPoint+Size);
}
//叉积
element cross( Point p1, Point p2 , Point p3 ){
return (p2.x-p1.x)*(p3.y-p1.y)-(p2.y-p1.y)*(p3.x-p1.x);
}
void work(){
init();
topUpper = topLower = 0;
for(int i = 0 ; i < Size ; ++ i ){
while( cross( upper[topUpper-2] , upper[ topUpper-1 ] , allPoint[i] ) > 0 && topUpper >= 2 )
topUpper--;//删除不属于凸包上的点
upper[ topUpper++ ] = allPoint[i] ;
}
for(int i = Size-1 ; i >= 0 ; i-- ){
while( cross( lower[topLower-2] , lower[ topLower-1 ] , allPoint[i] ) > 0 && topLower >= 2 )
topLower --;
lower[ topLower++ ] = allPoint[i] ;
}
/*for( int i = 1 ; i < topUpper-1 ; ++ i )
lower[ topLower ++ ] = upper[ i ] ;*/
}
}Solve;
//以下非模板
int main(){
int t;scanf("%d",&t);
while(t--){
int m,k;
int n;scanf("%d%d%d",&k,&m,&n);
Solve.Size = n;
for(int i = 0 ; i < n ; ++ i )
scanf("%d%d" , &Solve.allPoint[i].x , &Solve.allPoint[i].y );
if(n==1){
puts("0.00");continue;
}
if(n==2){
printf("%.2lf\n" , dist(Solve.allPoint[0],Solve.allPoint[1]));continue;
}
Solve.work();
double ret = 0;
for(int i = 0 ; i < Solve.topLower ; ++ i ){
ret += max(0,-Solve.lower[i].x*k+Solve.lower[i].y*m);
}
for(int i = 1 ; i < Solve.topUpper-1 ; ++ i ){
ret += max(0,-Solve.upper[i].x*k+Solve.upper[i].y*m);
}
printf("%.0lf\n" , ret);
}
return 0;
}
/*
12 7
30 5
80 7
50 87
22 9
45 1
50 7
*/
与队友共勉:如果还是不理解凸包,我把电子书传到讨论组,可以看第一章。