最小凸包算法
使用Graham扫描法进新解决最小凸包问题
先找到最左下端点
然后根据极角来进行逆时针排序
在根据相对极角增减来去除不需要的点
C++代码
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 #define PI 3.1415926535 7 using namespace std; 8 struct node 9 { 10 int x,y; 11 }; 12 node vex[1000];//存入的所有的点 13 node stackk[1000];//凸包中所有的点 14 int xx,yy; 15 bool cmp1(node a,node b)//排序找第一个点 16 { 17 if(a.y==b.y) 18 return a.x<b.x; 19 else 20 return a.y<b.y; 21 } 22 int cross(node a,node b,node c)//计算叉积 23 { 24 return (b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y); 25 } 26 double dis(node a,node b)//计算距离 27 { 28 return sqrt((a.x-b.x)*(a.x-b.x)*1.0+(a.y-b.y)*(a.y-b.y)); 29 } 30 bool cmp2(node a,node b)//极角排序另一种方法,速度快 31 { 32 if(atan2(a.y-yy,a.x-xx)!=atan2(b.y-yy,b.x-xx)) 33 return (atan2(a.y-yy,a.x-xx))<(atan2(b.y-yy,b.x-xx)); 34 return a.x<b.x; 35 } 36 bool cmp(node a,node b)//极角排序 37 { 38 int m=cross(vex[0],a,b); 39 if(m>0) 40 return 1; 41 else if(m==0&&dis(vex[0],a)-dis(vex[0],b)<=0) 42 return 1; 43 else return 0; 44 /*if(m==0) 45 return dis(vex[0],a)-dis(vex[0],b)<=0?true:false; 46 else 47 return m>0?true:false;*/ 48 } 49 int main() 50 { 51 int t,L; 52 while(~scanf("%d",&t),t) 53 { 54 int i; 55 for(i=0; i<t; i++) 56 { 57 scanf("%d%d",&vex[i].x,&vex[i].y); 58 } 59 if(t==1) 60 printf("%.2f\n",0.00); 61 else if(t==2) 62 printf("%.2f\n",dis(vex[0],vex[1])); 63 else 64 { 65 memset(stackk,0,sizeof(stackk)); 66 sort(vex,vex+t,cmp1); 67 stackk[0]=vex[0]; 68 xx=stackk[0].x; 69 yy=stackk[0].y; 70 sort(vex+1,vex+t,cmp2);//cmp2是更快的,cmp更容易理解 71 stackk[1]=vex[1];//将凸包中的第两个点存入凸包的结构体中 72 int top=1;//最后凸包中拥有点的个数 73 for(i=2; i<t; i++) 74 { 75 while(i>=1&&cross(stackk[top-1],stackk[top],vex[i])<0) //对使用极角排序的i>=1有时可以不用,但加上总是好的 76 top--; 77 stackk[++top]=vex[i]; //控制<0或<=0可以控制重点,共线的,具体视题目而定。 78 } 79 double s=0; 80 //for(i=1; i<=top; i++)//输出凸包上的点 81 //cout<<stackk[i].x<<" "<<stackk[i].y<<endl; 82 for(i=1; i<=top; i++) //计算凸包的周长 83 s+=dis(stackk[i-1],stackk[i]); 84 s+=dis(stackk[top],vex[0]);//最后一个点和第一个点之间的距离 85 /*s+=2*PI*L; 86 int ans=s+0.5;//四舍五入 87 printf("%d\n",ans);*/ 88 printf("%.2lf\n",s); 89 } 90 } 91 }
MATLAB代码
1 %% 本函数作用就是使用Graham扫描法进新解决最小凸包问题 2 3 function Stack = GrahamNew(Spots) 4 %% 任意画出坐标点 5 6 clc 7 clear all 8 close all 9 10 img=ones(256,256); 11 imshow(img); 12 [x,y]=ginput(); 13 x=round(x); 14 y=round(y); 15 n=length(x); 16 p=[]; 17 for i=1:n 18 img(y(i)-1:y(i)+1,x(i)-1:x(i)+1)=0; 19 p=[p;x(i) y(i)]; %待判断凸包的点集 20 end 21 imshow(img); 22 Spots = p; 23 24 25 N = size(Spots,1); 26 if N<3 % 点太少不符合要求 27 exit(); 28 end 29 %% 此函数的作用是给随机坐标点进行逆时针排序 30 % 找到最左下端点 31 Temp = [Spots(:,2) Spots(:,1)]; 32 Temp = sortrows(Temp); 33 X = Temp(1,2); 34 Y = Temp(1,1); 35 36 Angle = []; 37 for k = 1:N 38 dy = Spots(k,2) - Y; 39 dx = Spots(k,1) - X; 40 Angle = [Angle;mod(atan2(dy,dx), 2*pi)]; 41 end 42 NewSpots = [Angle,Spots]; 43 NewSpots = sortrows(NewSpots);% 完美解决了极角相同的点 44 NewSpots = NewSpots(:,2:3); 45 46 %% 使用栈进栈出原理将不符合要求的点去除 47 Stack = []; 48 Stack = [Stack;NewSpots(1:3,:)];% 压入前3个点 49 50 k = 4; 51 while(k<=N) 52 top = size(Stack,1);% 模拟栈顶指针 53 dy = Stack(top,2) - Stack(top-1,2); 54 dx = Stack(top,1) - Stack(top-1,1); 55 % 已存入最后线段的极角 56 theta1 = mod(atan2(dy,dx), 2*pi); 57 dy = NewSpots(k,2) - Stack(top,2); 58 dx = NewSpots(k,1) - Stack(top,1); 59 % 准备存入线段的角度 60 theta2 = mod(atan2(dy,dx), 2*pi); 61 if (theta1-theta2)<=0 62 Stack(top+1,:) = NewSpots(k,:);% 入栈 63 k = k+1; 64 else 65 Stack(top,:) = [];% 弹出栈 66 end 67 end 68 69 70 %% 画图测试 71 Spots = [Spots;Spots(1,:)]; 72 NewSpots = [NewSpots; NewSpots(1,:)]; 73 Stack = [Stack;Stack(1,:)]; 74 75 figure(1)% 原乱序点的图形 76 plot(Spots(:,1),Spots(:,2)); 77 axis([0,256,0,256]); 78 79 figure(2)% 排序点的图形 80 plot(NewSpots(:,1),NewSpots(:,2)); 81 axis([0,256,0,256]); 82 83 figure(3)% 优化后的凸形 84 plot(Stack(:,1),Stack(:,2)); 85 axis([0,256,0,256]); 86 87 end