原题链接:HDOJ1007
本来看到这样的题我应该是一脸懵逼的,但是幸运的是由于太菜没来得及做,导致做到这题的时候已经被科普解法了。没错!就是按照x+y排序 分治!
按照了解到的解法,我知道了每块区域要划分为左半区,右半区,一左一右的中间区。(还没学到分治
于是用到递归写出了这样的代码
#include "iostream"
#include <algorithm>
#include "math.h"
#include "string.h"
using namespace std;
struct poi;
struct poi{
double x,y;
};
poi a[999999];
//double dtc[9999][9999];
double dis(const poi &a, const poi &b){
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
bool eq(const double& a,const double& b){
if(fabs(a-b)<0.000000001) return true;
else return false;
}
double mindis(int start,int end){//一个区域中的最小距离=min(左半区最小距离,右边区最小距离,一左一右最小距离)
int cap=end-start+1;
int left=start,right=start+cap/2;
int leftcap=cap/2,rightcap=cap-cap/2,lefte=start+leftcap-1,righte=right+rightcap-1;
if(cap==1) return 1e+300;
else if(cap==2) return dis(a[start],a[end]);
double minlr=min(mindis(left,lefte),mindis(right,righte));//左半区和右半区最小距离
//计算中间区域最小距离
int i,j;
for(i=right;(fabs(a[i].x-a[right].x))<minlr||eq(a[right].x-a[i].x,minlr);i--){if(i<left) break;}//令i+1为中间区域的第一个物体
for(j=right;(fabs(a[j].x-a[right].x))<minlr||eq(a[j].x-a[right].x,minlr);j++){if(j>righte) break;}//令j-1 为中央区域的最后一个物体
i++;j--;
if(i!=j) {
double minmid = dis(a[i], a[j]);
for (int k = i; k <= j&&k<=lefte; k++)
for (int t = right; t>=right&&t <= j; t++) {
if (dis(a[k], a[t]) < minmid) minmid = dis(a[k], a[t]);
}
return min(minmid, minlr);
}
else return minlr;
}
bool cmp(const poi& x,const poi &y){
return x.x<y.x;
}
int main(){
int N;
while(cin>>N&&N!=0){
memset(a,0, sizeof(poi)*N);
for(int i=0;i<N;i++) {cin>>a[i].x>>a[i].y;}
sort(a,a+N,cmp);
printf("%.2lf\n",mindis(0,N-1)/2);
}
return 0;
}
交上去之后TLE了。。。
于是想到是不是求中间区域的时候项太多了
于是将求中间区域的代码改成递归
//计算中间区域最小距离
int i,j;
for(i=right;(fabs(a[i].x-a[right].x))<minlr||eq(a[right].x-a[i].x,minlr);i--){if(i<left) break;}//令i+1为中间区域的第一个物体
for(j=right;(fabs(a[j].x-a[right].x))<minlr||eq(a[j].x-a[right].x,minlr);j++){if(j>righte) break;}//令j-1 为中央区域的最后一个物体
i++;j--;
return min(mindis(i,j),minlr)
然后发现毫无l用,对于一些数据可以无限递归下去
这时我坚信问题就出在这个中间区域中,于是跑去百度
果不其然
在这里找到了关于中间区域的解法
https://blog.csdn.net/lishuhuakai/article/details/9161843
然后我就把中间区域的代码改成了这样:
//计算中间区域最小距离
int i,j;
for(i=right;(fabs(a[i].x-a[right].x))<minlr||eq(a[right].x-a[i].x,minlr);i--){if(i<left) break;}//令i+1为中间区域的第一个物体
for(j=right;(fabs(a[j].x-a[right].x))<minlr||eq(a[j].x-a[right].x,minlr);j++){if(j>righte) break;}//令j-1 为中央区域的最后一个物体
i++;j--;
if(i!=j) {
int p=0;
for(int k=i;k<right;k++) {m[p].x=a[k].x;m[p].y=a[k].y;m[p].id=0;p++;}
for(int k=right;k<=j;k++) {m[p].x=a[k].x;m[p].y=a[k].y;m[p].id=1;p++;}//将中间区域的点放入m数组中以便排序
sort(m,m+p,cmpy);//对中间区域的店按y进行排序
for(int k=0;k<p;k++)//从中间区域中每一点都找出之后7个点,与它们求最短距离
for(int t=1;(k+t)<p&&t<8;t++){
if(dis(m[k],m[k+t])<minlr&&m[k].id!=m[k+t].id) minlr=dis(m[k],m[k+t]);
}
return minlr;
}
else return minlr;
然后!
还是TLE~~
这时看着那篇博客的我一脸茫然,这不是和他写的思路一样了吗?为什么他就能过我过不了
于是我先生成了10w对随机数
int main() {
double a,b;
srand(time(0));
for(int i=0;i<100000;i++) {
a=(rand()%999+1)*1.0;
b=(rand()%999+1)*1.0;
cout<<a<<' '<<b<<endl;
}
}
再将它们输入到程序里,发现
根本发现不了什么
然后我又注释掉这里
// memset(a,0, sizeof(poi)*N);
// memset(m,0, sizeof(m));
仍然TLE
后来我又找到了百度
继续搜索帖子,然后我在一个帖子里注意到了这里
(不会是这里吧
|
|
|
AC
至于为什么之前那个帖子的代码可以AC,我也有同样的好奇
后来我再把他的代码提交,发现TLE
没人看的代码附上:
#include "iostream"
#include <algorithm>
#include "math.h"
#include "string.h"
using namespace std;
struct poi;
struct poi{
double x,y;
};
struct poi_id{
double x,y;
int id;
};
poi a[999999];
//double dtc[9999][9999];
poi_id m[100100];
bool cmpx(const poi& x, const poi &y){
return x.x<y.x;
}
bool cmpy(const poi_id &x,const poi_id &y){
return x.y<y.y;
}
template <class T>
double dis(T &a, T &b){
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
bool eq(const double& a,const double& b){
if(fabs(a-b)<0.000000001) return true;
else return false;
}
double mindis(int start,int end){//一个区域中的最小距离=min(左半区最小距离,右边区最小距离,一左一右最小距离)
int cap=end-start+1;
int left=start,right=start+cap/2;
int leftcap=cap/2,rightcap=cap-cap/2,lefte=start+leftcap-1,righte=right+rightcap-1;
if(cap==1) return 1e+300;
else if(cap==2) return dis(a[start],a[end]);
else if(cap==3) return min(min(dis(a[start],a[end]),dis(a[start+1],a[end])),dis(a[start+1],a[start]));
double minlr=min(mindis(left,lefte),mindis(right,righte));//左半区和右半区最小距离
//计算中间区域最小距离
int i,j;
for(i=right;(fabs(a[i].x-a[right].x))<minlr||eq(a[right].x-a[i].x,minlr);i--){if(i<left) break;}//令i+1为中间区域的第一个物体
for(j=right;(fabs(a[j].x-a[right].x))<minlr||eq(a[j].x-a[right].x,minlr);j++){if(j>righte) break;}//令j-1 为中央区域的最后一个物体
i++;j--;
if(i!=j) {
int p=0;
for(int k=i;k<right;k++) {m[p].x=a[k].x;m[p].y=a[k].y;m[p].id=0;p++;}
for(int k=right;k<=j;k++) {m[p].x=a[k].x;m[p].y=a[k].y;m[p].id=1;p++;}//将中间区域的点放入m数组中以便排序
sort(m,m+p,cmpy);//对中间区域的店按y进行排序
for(int k=0;k<p;k++)//从中间区域中每一点都找出之后7个点,与它们求最短距离
for(int t=1;(k+t)<p&&t<8;t++){
if(dis(m[k],m[k+t])<minlr&&m[k].id!=m[k+t].id) minlr=dis(m[k],m[k+t]);
}
return minlr;
}
else return minlr;
}
int main(){
int N;
while(cin>>N&&N!=0){
// memset(a,0, sizeof(poi)*N);
// memset(m,0, sizeof(m));
for(int i=0;i<N;i++) {scanf("%lf%lf",&a[i].x,&a[i].y);}
sort(a, a + N, cmpx);
printf("%.2lf\n",mindis(0,N-1)/2);
}
return 0;
}