hdu 1007 Quoit Design 求解最近对问题
Problem Description Have you ever played quoit in a playground? Quoit is a game in which flat rings are pitched at some toys, with all the toys encircled awarded. In the field of Cyberground, the position of each toy is fixed, and the ring is carefully designed so it can only encircle one toy at a time. On the other hand, to make the game look more attractive, the ring is designed to have the largest radius. Given a configuration of the field, you are supposed to find the radius of such a ring. Assume that all the toys are points on a plane. A point is encircled by the ring if the distance between the point and the center of the ring is strictly less than the radius of the ring. If two toys are placed at the same point, the radius of the ring is considered to be 0. Input The input consists of several test cases. For each case, the first line contains an integer N (2 <= N <= 100,000), the total number of toys in the field. Then N lines follow, each contains a pair of (x, y) which are the coordinates of a toy. The input is terminated by N = 0. Output For each test case, print in one line the radius of the ring required by the Cyberground manager, accurate up to 2 decimal places. Sample Input 2 0 0 1 1 2 1 1 1 1 3 -1.5 0 0 0 0 1.5 0 Sample Output 0.71 0.00 0.75
http://acm.hdu.edu.cn/showproblem.php?pid=1007
这一题本身不难,但因为细节折磨了我四个多小时,因此不得不记下来。
解题思路:
如算法书上所言,使用了分治法的思想,递归求解最近对。同时注意“分治”得出的最近对并不一定就是最终的最近对。最近对有可能是中分线两侧的点组成的,因此需要再判断。
AC代码:
1 #include <iostream> 2 #include <algorithm> 3 #include <vector> 4 #include <cmath> 5 using namespace std; 6 7 typedef struct NODE 8 { 9 double x,y; 10 bool operator<(const struct NODE &t) 11 { 12 if(x < t.x) 13 return true; 14 else if(x == t.x && y < t.y) 15 return true; 16 return false; 17 } 18 }Node; 19 20 double getDist(const Node &a,const Node &b) 21 {// 获取两点之间的距离 22 return sqrt(pow(a.x - b.x,2) + pow(a.y - b.y,2)); 23 } 24 25 double getMin(const vector<Node> &ivec,int start,int end) 26 {// 递归求最近对距离 27 28 int mid = (start + end) >> 1; 29 30 // assert(start >= 0);// 断言,范围检测 31 // assert(end < ivec.size()); 32 // assert(start <= end); 33 34 if(mid == start)// 有1个或两个点 35 return getDist(ivec[start],ivec[end]); 36 else if(mid + 1 == end) 37 {// 有三个点 38 double d1 = getDist(ivec[start],ivec[start + 1]); 39 double d2 = getDist(ivec[start],ivec[end]); 40 double d3 = getDist(ivec[start + 1],ivec[end]); 41 return min(min(d1,d2),d3); 42 } 43 else 44 { 45 // 求出中线两边集合各自的最近对的最小值d 46 double d = min( getMin(ivec,start,mid) , getMin(ivec,mid + 1,end) ); 47 vector<Node> tvec,rvec; 48 Node t; 49 for(unsigned i = start;i <= end;i ++) 50 { 51 if(fabs(ivec[i].x - ivec[mid].x) < d) 52 tvec.push_back(ivec[i]);// 记录距离中线小于最小距离d的所有点,从star到end遍历,tvec中的元素已经排序 53 } 54 int len = tvec.size(); 55 mid = len / 2; 56 for(unsigned i = 0;i < len;i ++) 57 { 58 if(fabs(tvec[i].y - tvec[mid].y) < d) 59 rvec.push_back(tvec[i]);// 找出所有组成直线可能小于d的点 60 } 61 len = rvec.size(); 62 for( unsigned i = 0;i < len - 1; i ++ ) 63 { 64 for(unsigned j = i + 1;j < len;j ++) 65 { 66 d = min(d,getDist(rvec[i],rvec[j])); 67 } 68 } 69 return d; 70 } 71 72 } 73 74 int main() 75 { 76 vector<Node> ivec; 77 unsigned N; 78 double minDist; 79 Node node; 80 while((scanf("%d",&N) != EOF) && N) 81 { 82 ivec.clear(); 83 for( unsigned i = 0;i < N; ++ i ) 84 { 85 scanf("%lf%lf",&node.x,&node.y); 86 ivec.push_back(node); 87 } 88 sort(ivec.begin(),ivec.end()); 89 printf("%.2lf\n",( getMin(ivec,0,ivec.size() - 1 )) / 2); 90 } 91 return 0; 92 }
其实代码很快就都敲出来了,就算慢也不过半小时,但是代码提交后一直都是Time Limit Exceeded!但是后面三点多一直到晚上7点多,我最后才调试出错在哪里!!!头都想爆了。原因就是在getMin函数中,把len变量声明为unsigned了。
本来这是一个我认为是的好习惯,只要变量恒大于0,就使用unsigned而不用int,但是我错了。这一习惯已经害了我几次了,这一次害得特别深刻。经过前几次,我得出了经验:使用unsigned不要用--len之类的减法语句,因为这个变量恒大于0,使用减法容易导致出现无穷大。这一次我也理所当然的没有用减法,但是有意无意我就写下了第62行的代码代码:
1 for( unsigned i = 0;i < len - 1; i ++ ) 2 { 3 for(unsigned j = i + 1;j < len;j ++) 4 { 5 d = min(d,getDist(rvec[i],rvec[j])); 6 } 7 }
当len为0时,就死循环了!!!这也导致程序提交结果一直都是TLE!!如果在自己电脑上还好,在杭电上就很难测试出错误在哪里了。
吸取教训,以后宁愿使用long型也不实用unsigned了,因为都习惯了加上下界判断,让变量拥有一个“负数”的特性其实也不错。
另外以上代码run了一下用时1700+MS,如果不用STL速度可以到1200+MS。如下:
View Code
#include <iostream> #include <algorithm> #include <cmath> #include <cstdio> using namespace std; #define MAX 100001 typedef struct NODE { double x,y; bool operator<(const struct NODE &t) { if(x == t.x) return y < t.y; return x < t.x; } }Node; Node ivec[MAX],tvec[MAX],rvec[MAX]; double getDist(const Node &a,const Node &b) {// 获取两点之间的距离 return sqrt(pow(a.x - b.x,2) + pow(a.y - b.y,2)); } double getMin(int start,int end) {// 递归求最近对距离 assert(start >= 0);// 断言,范围检测 assert(end < ivec.size()); assert(start <= end); if(start == end) return 0; else if(start + 1 == end)// 有1个或两个点 return getDist(ivec[start],ivec[end]); else if(start + 2 == end) {// 有三个点 double d1 = getDist(ivec[start],ivec[start + 1]); double d2 = getDist(ivec[start],ivec[end]); double d3 = getDist(ivec[start + 1],ivec[end]); return min(min(d1,d2),d3); } else { int mid = (start + end) / 2; // // 求出中线两边集合各自的最近对的最小值d double d = min( getMin(start,mid) , getMin(mid + 1,end) ); // // vector<Node> tvec,rvec; // // Node t; unsigned i,k; for(i = start,k = 0;i <= end;i ++) { if(fabs(ivec[i].x - ivec[mid].x) < d) tvec[k++] = ivec[i];// 记录距离中线小于最小距离d的所有点,从star到end遍历,tvec中的元素已经排序 } sort(tvec,tvec + k); int len = k; mid = (k) / 2; for(i = 0,k = 0;i < len;i ++) { if(fabs(tvec[i].y - tvec[mid].y) < d) rvec[k++] = tvec[i]; } len = k; for( int i = 0;i < len - 1; i ++ ) { for(int j = i + 1;j < len;j ++) { d = min(d,getDist(rvec[i],rvec[j])); } } return d; } } int main() { unsigned N; while((scanf("%d",&N) != EOF) && N) { for( unsigned i = 0;i < N; ++ i ) { scanf("%lf%lf",&ivec[i].x,&ivec[i].y); } sort(ivec,ivec + N); printf("%.2lf\n",( getMin(0,N - 1)) / 2); } return 0; }