POJ1696-Space Ant
转载请注明出处:優YoU http://user.qzone.qq.com/289065406/blog/1302880751
大致题意:
一只蚂蚁,只会向左转,现在给出平面上很多个点,求解一种走法,
能使得蚂蚁能经过的点最多,每个顶点该蚂蚁只能经过一次,且所行走的路线不能发生交叉.
解题思路:
凸包的入门水题,是凸包的一个变形
网上看到很多人copy别人的,说什么“极坐标排序”,那是Graham Scan Algoruthm的做法!虽然Graham只有O(nlogn) ,但是这题完全没必要用它,因为题目的规模很小,我用卷包裹算法照样0 ms 一次AC 。确实理论上卷包裹的O(n^2)不如Graham快,但是规模这么小的题目是反映不出来的。
Graham能不用就不用,一代码超长超烦,特别是散点集排序。看过算法理论的同学,一般都宁愿用极坐标而不用水平序,但是极坐标排序的比较规则特容易出错。水平排序我没看懂,估计其他菜鸟们也差不多。
解题的例图题目已经给出,我就不贴了,反正最终的路线就是螺旋形的。要求从 纵坐标 最小的点作为出发点,这个在输入时顺便找出来,可以节省一点点时间= =
其实无论输入什么样的点集,一定可以走完全部n个点的,这是凸包的性质决定,所以就放手去做了,不用担心走不完的情况
唯一注意的是,前面提到过时凸包的变形,是因为螺旋线是不封闭的,凸包是封闭的。要想不封闭很简单,做一个标记就可以了,每构造一个凸包顶点,就标记该点,不再与其连线
当连线完前面n-1个点后,第n个点(最后没被标记的点)就不用再做了,一定是最后一点,输出它就可以了
其他细节参见我的程序, 卷包裹算法 百度就有了,这里我就不多说了
1 //Memory Time
2 //228K 0MS
3
4 #include<iostream>
5 using namespace std;
6
7 const int inf=101;
8
9 typedef class
10 {
11 public:
12 int x,y;
13 }point;
14
15 /*AB距离平方*/
16
17 int distsquare(point A,point B)
18 {
19 return (B.x-A.x)*(B.x-A.x)+(B.y-A.y)*(B.y-A.y);
20 }
21
22 /*叉积计算*/
23
24 int det(int x1,int y1,int x2,int y2)
25 {
26 return x1*y2-x2*y1;
27 }
28
29 int cross(point A,point B,point C,point D)
30 {
31 return det(B.x-A.x , B.y-A.y , D.x-C.x , D.y-C.y);
32 }
33
34 int main(int i)
35 {
36 int test;
37 cin>>test;
38 while(test--)
39 {
40 int n; //n个点
41 cin>>n;
42 point* node=new point[n+1]; //n个点坐标
43 int* conbag=new int[n+1]; //凸包顶点(根据顶点构造顺序,依次记录node[]下标)
44 bool* vist=new bool[n+1]; //标记已作为凸包顶点的点
45
46 /*Input*/
47
48 int min_y=inf; //最小纵坐标值
49 int fi=0;
50 for(i=1;i<=n;i++)
51 {
52 int num;
53 cin>>num>>node[i].x>>node[i].y;
54 vist[i]=false;
55
56 if(min_y > node[i].y)
57 {
58 min_y = node[i].y;
59 fi=i;
60 }
61 }
62 conbag[1]=fi; //起点
63 conbag[0]=1; //凸包顶点计数器
64 vist[fi]=true;
65
66 /*Structure Convex bag*/
67
68 int pc=1; //conbag[]指针
69 while(true)
70 {
71 int s=conbag[pc]; //最新的凸包顶点
72 int k; //当前待加入的凸包顶点
73 for(i=1;i<=n;i++) //寻找当前基准向量sk,k取任意没标记的点就可以了
74 if(!vist[i])
75 {
76 k=i;
77 break;
78 }
79
80 for(i=1;i<=n;i++) //枚举没标记的点i,计算sk X si的值,寻找最优(最外面的)k点作为凸包顶点
81 if(i!=k && !vist[i])
82 {
83 int temp=cross(node[s],node[k],node[s],node[i]);
84
85 if(temp<0)
86 k=i;
87 else if(temp==0)
88 if(distsquare(node[s],node[k]) > distsquare(node[s],node[i])) //当k i共线时,距离s近的点优先选取
89 k=i;
90 }
91
92 conbag[++pc]=k; //更新凸包顶点
93 conbag[0]++;
94 vist[k]=true;
95
96 if(n-conbag[0]==1) //处理完前面n-1个点后 第n个点不再处理
97 break;
98 }
99
100 cout<<conbag[0]+1<<' '; //这里输出n也可以的
101
102 fi=0;
103 for(i=1;i<=conbag[0];i++)
104 {
105 cout<<conbag[i]<<' '; //输出前面n-1个点的同时寻找第n个没标记的点
106 if(!vist[i])
107 fi=i;
108 }
109 if(fi)
110 cout<<fi<<endl;
111 else
112 cout<<n<<endl;
113
114 delete node;
115 delete conbag;
116 delete vist;
117
118 }
119 return 0;
120 }