日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

曼哈顿距离最小生成树

發(fā)布時間:2023/12/15 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 曼哈顿距离最小生成树 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一、參考博客

博客:曼哈頓距離最小生成樹與莫隊算法

博客:學習總結:最小曼哈頓距離生成樹

二、前置知識

1.曼哈頓距離:給定二維平面上的N個點,在兩點之間連邊的代價。(即distance(P1,P2) = |x1-x2|+|y1-y2|)

2.曼哈頓距離最小生成樹問題求什么?求使所有點連通的最小代價。

3.最小生成樹

三、具體實現(xiàn)方式

樸素的算法可以用O(N2)的Prim,或者處理出所有邊做Kruskal,但在這里總邊數(shù)有O(N2)條,所以Kruskal的復雜度變成了O(N2logN)。

但是事實上,真正有用的邊遠沒有O(N2)條。我們考慮每個點會和其他一些什么樣的點連邊。

可以得出這樣一個結論:以一個點為原點建立直角坐標系,在每45度內只會向距離該點最近的一個點連邊。

證明結論:假設我們以點A為原點建系,考慮在y軸向右45度區(qū)域內的任意兩點B(x1,y1)和C(x2,y2),不妨設|AB|≤|AC|(這里的距離為曼哈頓距離),如下圖:

|AB|=x1+y1,|AC|=x2+y2,|BC|=|x1-x2|+|y1-y2|。而由于B和C都在y軸向右45度的區(qū)域內,有y-x>0且x>0。下面我們分情況討論:

x1>x2且y1>y2。這與|AB|≤|AC|矛盾;
x1≤x2且y1>y2。此時|BC|=x2-x1+y1-y2,|AC|-|BC|=x2+y2-x2+x1-y1+y2=x1-y1+2y2。由前面各種關系可得y1>y2>x2>x1。假設|AC|<|BC|即y1>2y2+x1,那么|AB|=x1+y1>2x1+2y2,|AC|=x2+y2<2*y2<|AB|與前提矛盾,故|AC|≥|BC|;
x1>x2且y1≤y2。與2同理;
x1≤x2且y1≤y2。此時顯然有|AB|+|BC|=|AC|,即有|AC|>|BC|。
綜上有|AC|≥|BC|,也即在這個區(qū)域內只需選擇距離A最近的點向A連邊。

這種連邊方式可以保證邊數(shù)是O(N)的,那么如果能高效處理出這些邊,就可以用Kruskal在O(NlogN)的時間內解決問題。下面我們就考慮怎樣高效處理邊。

我們只需考慮在一塊區(qū)域內的點,其他區(qū)域內的點可以通過坐標變換“移動”到這個區(qū)域內。為了方便處理,我們考慮在y軸向右45度的區(qū)域。在某個點A(x0,y0)的這個區(qū)域內的點B(x1,y1)滿足x1≥x0且y1-x1>y0-x0。這里對于邊界我們只取一邊,但是操作中兩邊都取也無所謂。那么|AB|=y1-y0+x1-x0=(x1+y1)-(x0+y0)。在A的區(qū)域內距離A最近的點也即滿足條件的點中x+y最小的點。因此我們可以將所有點按x坐標排序,再按y-x離散,用線段樹或者樹狀數(shù)組維護大于當前點的y-x的最小的x+y對應的點。時間復雜度O(NlogN)。

至于坐標變換,一個比較好處理的方法是第一次直接做;第二次沿直線y=x翻轉,即交換x和y坐標;第三次沿直線x=0翻轉,即將x坐標取相反數(shù);第四次再沿直線y=x翻轉。注意只需要做4次,因為邊是雙向的。

至此,整個問題就可以在O(NlogN)的復雜度內解決了。

【回到正題】

一個點把平面分成了8個部分:

由上面的廢話可知,我們只需要讓這個點與每個部分里距它最近的點連邊。

拿R1來說吧:

如圖,i的R1區(qū)域里距i最近的點是j。也就是說,其他點k都有:

xj + yj <= xk + yk

那么k將落在如下陰影部分:

顯然,邊(i,j), (j,k), (i,k)構成一個環(huán)<i,j,k>,而(i,k)一定是最長邊,可以被刪去。所以我們只連邊(i,j)。

為了避免重復加邊,我們只考慮R1~R4這4個區(qū)域。(總共加了4N條邊)

這4個區(qū)域的點(x,y)要滿足什么條件?

如果點(x,y)在R1,它要滿足:x ≥ xi ,y – x ≥ yi – xi(最近點的x + y最小)
如果點(x,y)在R2,它要滿足:y ≥ yi ,y – x ≤ yi – xi(最近點的x + y最小)
如果點(x,y)在R3,它要滿足:y ≤ yi ,y + x ≥ yi + xi(最近點的y – x最小)
如果點(x,y)在R4,它要滿足:x ≥ xi ,y + x ≤ yi – xi(最近點的y – x最小)
其中一個條件用排序,另一個條件用數(shù)據(jù)結構(這種方法很常用),在數(shù)據(jù)結構上詢問,找最近點。因為詢問總是前綴或后綴,所以可以用樹狀數(shù)組。

#include<iostream> #include<algorithm> #include<cstdio> #include<cstdlib> #include<cstring> #include<string> #include<cmath> #include<queue> #include<bitset> #include<set> #include<stack> #include<map> #include<list> #include<new> #include<vector> #define MT(a,b) memset(a,b,sizeof(a)); using namespace std; typedef long long ll; typedef unsigned long long ull; const double pi=acos(-1.0); const double E=2.718281828459; const ll mod=1e8+7; const int INF=0x3f3f3f3f;int n,k;struct node{int x;int y;int id;bool friend operator<(node a,node b){return a.x==b.x?a.y<b.y:a.x<b.x;///保證樹狀數(shù)組更新和查詢時不會遺漏} }point[10005];struct edge{int s;int e;int c;bool friend operator<(edge a,edge b){return a.c<b.c;} }load[40000]; int sign=0; int p[10005]; int find(int x){return p[x]==x?x:p[x]=find(p[x]); }void kruskal(){for(int i=1;i<=n;i++)p[i]=i;sort(load+1,load+1+sign);int cnt=0;for(int i=1;i<=sign;i++){int x=find(load[i].s);int y=find(load[i].e);if(x!=y){cnt++;p[x]=y;if(cnt==n-k){printf("%d\n",load[i].c);return ;}}} }int id[10005]; ///y-x為索引的編號 int xy[10005]; ///y-x為索引 x+y的最小值void update(int index,int minn,int s) ///index:y-x minn:x+y s:編號 {index+=1000;for(int i=index;i>=1;i-=(i&(-i))){if(xy[i]>minn){xy[i]=minn;id[i]=s;}} }void query(int index,int minn,int s) ///index:y-x minn:x+y s:編號 {index+=1000;int e=-1,c=INF;///現(xiàn)在以編號s為原點,查詢y-x>=index的點中x+y的最小值for(int i=index;i<10000;i+=(i&(-i))){if(xy[i]<c){e=id[i];c=xy[i];}}if(e!=-1)load[++sign]=edge{s,e,c-minn}; }void build_edge() {/// 以(xi,yi)為原點,對于第1區(qū)域內的點(x,y)滿足條件/// x>=xi,y-x>=yi-xi,(x+y)最小sort(point+1,point+1+n);memset(id,-1,sizeof(id));fill(xy,xy+10005,INF);///按照x升序///保證后面查詢時,x都比當前的x大for(int i=n;i>=1;i--){int index=point[i].y-point[i].x;int minn=point[i].x+point[i].y;query(index,minn,point[i].id);update(index,minn,point[i].id);} }int main() ///第K大邊 {scanf("%d %d",&n,&k);for(int i=1;i<=n;i++){scanf("%d %d",&point[i].x,&point[i].y);point[i].id=i;}///1象限建邊build_edge();///2象限建邊for(int i=1;i<=n;i++)swap(point[i].x,point[i].y);build_edge();///3象限建邊for(int i=1;i<=n;i++)point[i].x=-point[i].x;build_edge();///4象限建邊for(int i=1;i<=n;i++)swap(point[i].x,point[i].y);build_edge();kruskal();return 0; }

總結

以上是生活随笔為你收集整理的曼哈顿距离最小生成树的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。