使用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