这道题大概有两个基本算法,但因为水平有限,看不大懂怎么来用线段树实现,所以我是参考了分治的算法,下面是原题解博客:http://www.cnblogs.com/ccz181078/p/5603283.html
这个是用线段树解决的题解博客:http://www.cnblogs.com/clrs97/p/4872267.html
如果要理解这个算法的话,最好先去看一下如何实现最近点对,因为此算法与最近点对极为相似,只是在枚举时多算了一个点,复杂度为O(nlog^2n).
最近点对博客地址:http://blog.csdn.net/lonelycatcher/article/details/7973046/
唔,看过博客后,下面就是实现的代码,那具体解释在程序注释中。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<cstdlib> using namespace std; int addmax,addmin,submax,submin,xmin,xmax,ymin,ymax,maxans,minans; int t,n; struct p{ int x,y; }a[200000],b[200000]; int calc(p a1,p b1,p c1) { int sum=max(max(a1.x,b1.x),c1.x)-min(min(a1.x,b1.x),c1.x)+max(max(a1.y,b1.y),c1.y)-min(min(a1.y,b1.y),c1.y); return sum;//这边是计算三个点的曼哈顿值。 } bool cmp1(const p&a,const p&b){return (a.x==b.x?a.y<b.y:a.x<b.x);} bool cmp2(const p&a,const p&b){return (a.y==b.y?a.x<b.x:a.y<b.y);} void get(int l,int r) { if (r-l<15) {//当点数小于15时,我们可以直接来枚举其中的三个点,因为时间复杂度允许。 for (int i=l;i<=r;i++) for (int j=i+1;j<=r;j++) for (int k=j+1;k<=r;k++) minans=min(minans,calc(a[i],a[j],a[k])); //直接枚举此区间中的最小曼哈顿距离 return; } int mid=(l+r)/2; get(l,mid); get(mid,r); //二分处理 //接下来是合并左右两个区间了 t=0; for (int i=mid;i<=r;i++) if (abs(a[i].x-a[mid].x)<minans) b[++t]=a[i]; else break; //找到右边x坐标差小于已找到的答案的点并保存下来 for (int i=mid-1;i>=l;i--) if (abs(a[i].x-a[mid].x)<minans) b[++t]=a[i]; else break; //找右边的,因为之前排序过,所以如果当前不符合,接下来的也一定不符合。 sort(b+1,b+t+1,cmp2);//又根据y轴排一遍序 int nowl=1; for (int i=3;i<=t;i++)//i是最上边的点,即y最大的点 { while (abs(b[nowl].y-b[i].y)>=minans) nowl++;//如果y值太小也删掉 for (int j=nowl;j<i;j++) for (int k=j+1;k<i;k++)//枚举接下来两个点 minans=min(minans,calc(b[j],b[k],b[i])); //计算更新 } } int main() { scanf("%d",&n); minans=ymin=xmin=submin=addmin=1<<30; maxans=ymax=xmax=submax=addmax=-(1<<30); for (int i=1;i<=n;i++) { scanf("%d%d",&a[i].x,&a[i].y); //我比较奇怪的是,原博客说为了防止被卡,要将x与y交换,但原谅我完全不知道为什么。。。 //接下来我们们先提前处理好一些最大最小值,这样我们就可以直接算出最大的曼哈顿距离 xmin=min(xmin,a[i].x); xmax=max(xmax,a[i].x); ymin=min(ymin,a[i].y); ymax=max(ymax,a[i].y); addmax=max(addmax,a[i].x+a[i].y); addmin=min(addmin,a[i].x+a[i].y); submax=max(submax,a[i].x-a[i].y); submin=min(submin,a[i].x-a[i].y); } //addmax和submax的存在是为了保证我们所计算出的最大曼哈顿距离中一定包括了三个点。 sort(a+1,a+1+n,cmp1);//先以x坐标为第一关键字排序 maxans=max(maxans,addmax-xmin-ymin); maxans=max(maxans,xmax+ymax-addmin); maxans=max(maxans,submax+ymax-xmin); maxans=max(maxans,xmax-ymin-submin); printf("%d\n",maxans*2);//输出最大值 get(1,n);//开始分治最小值了 printf("%d\n",minans*2); return 0; }
那目前我还没有看懂别人线段树的题解,若看懂了,再写吧。。。
(原谅我这个自己想不出思路的人。。。)