**:
一、前人種樹
部落格:曼哈頓距離最小生成樹與莫隊演算法
部落格:學習總結:最小曼哈頓距離生成樹
二、知識梳理
曼哈頓距離:給定二維平面上的n個點,在兩點之間連邊的代價。(即distance(p1,p2) = |x1-x2|+|y1-y2|)
曼哈頓距離最小生成樹問題求什麼?求使所有點連通的最小代價。
最小生成樹的「環切」性質:在圖g = (v, e)中,如果存在乙個環,那麼把環上的最大邊e刪除後得到的圖g』 = (v, e- )的最小生成樹的邊權和與g相同。
三、難點剖析
【廢話定理神馬的,很難懂只要記住就是了】
樸素的演算法可以用o(n2)的prim,或者處理出所有邊做kruskal,但在這裡總邊數有o(n2)條,所以kruskal的複雜度變成了o(n2logn)。
但是事實上,真正有用的邊遠沒有o(n2)條。我們考慮每個點會和其他一些什麼樣的點連邊。
可以得出這樣乙個結論:以乙個點為原點建立直角座標系,在每45度內只會向距離該點最近的乙個點連邊。
證明結論:假設我們以點a為原點建系,考慮在y軸向右45度區域內的任意兩點b(x1,y1)和c(x2,y2),不妨設|ab|≤|ac|(這裡的距離為曼哈頓距離),如下圖:
|ab|=x1+y1,|ac|=x2+y2,|bc|=|x1-x2|+|y1-y2|。而由於b和c都在y軸向右45度的區域內,有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+2*y2。由前面各種關係可得y1>y2>x2>x1。假設|ac|2*y2+x1,那麼|ab|=x1+y1>2*x1+2*y2,|ac|=x2+y2<2*y2
x1>x2且y1≤y2。與2同理;
x1≤x2且y1≤y2。此時顯然有|ab|+|bc|=|ac|,即有|ac|>|bc|。
綜上有|ac|≥|bc|,也即在這個區域內只需選擇距離a最近的點向a連邊。
這種連邊方式可以保證邊數是o(n)的,那麼如果能高效處理出這些邊,就可以用kruskal在o(nlogn)的時間內解決問題。下面我們就考慮怎樣高效處理邊。
我們只需考慮在一塊區域內的點,其他區域內的點可以通過座標變換「移動」到這個區域內。為了方便處理,我們考慮在y軸向右45度的區域。在某個點a(x0,y0)的這個區域內的點b(x1,y1)滿足x1≥x0且y1-x1>y0-x0。這裡對於邊界我們只取一邊,但是操作中兩邊都取也無所謂。那麼|ab|=y1-y0+x1-x0=(x1+y1)-(x0+y0)。在a的區域內距離a最近的點也即滿足條件的點中x+y最小的點。因此我們可以將所有點按x座標排序,再按y-x離散,用線段樹或者樹狀陣列維護大於當前點的y-x的最小的x+y對應的點。時間複雜度o(nlogn)。
至於座標變換,乙個比較好處理的方法是第一次直接做;第二次沿直線y=x翻轉,即交換x和y座標;第三次沿直線x=0翻轉,即將x座標取相反數;第四次再沿直線y=x翻轉。注意只需要做4次,因為邊是雙向的。
至此,整個問題就可以在o(nlogn)的複雜度內解決了。
【回到正題】
乙個點把平面分成了8個部分:
由上面的廢話可知,我們只需要讓這個點與每個部分裡距它最近的點連邊。
拿r1來說吧:
如圖,i的r1區域裡距i最近的點是j。也就是說,其他點k都有:
xj + yj <= xk + yk
那麼k將落在如下陰影部分:
顯然,邊(i,j), (j,k), (i,k)構成乙個環,而(i,k)一定是最長邊,可以被刪去。所以我們只連邊(i,j)。
為了避免重複加邊,我們只考慮r1~r4這4個區域。(總共加了4n條邊)
這4個區域的點(x,y)要滿足什麼條件?
其中乙個條件用排序,另乙個條件用資料結構(這種方法很常用),在資料結構上詢問,找最近點。因為詢問總是字首或字尾,所以可以用樹狀陣列。
poj3241object clustering
#include#include#includeusing namespace std;
const int n = 1e5 + 5;
const int inf = 0x3f3f3f3f;
struct edge
} e[n<<3];
struct point
} p[n];
struct lsh
} lsh[n];
int a[n], f[n];
int mi[n], id[n];
int n, c, sz, tot, cnt;
int lowbit (int x)
int query(int x)
return ans;}
void modify(int x, int mi, int id)
}//bit維護的是某數字代表的區間的x+y最小值, 若一區間的不同位置最小值不同, 該區間則沒有最小值(即mi陣列維護的是其表示的區間都可以取到的最小值)
int find(int x)
void join(int x, int y)
void init ()}
int abs(int x, int y)
int dts(int x, int y)
void add_edge (int x, int y, int d)
int main()
for (int cas = 1; cas <= 4; ++cas)
}for (int i = 1; i <= n; ++i) f[i] = i;
sort(e + 1, e + sz + 1);
for (int i = 1; i <= sz; ++i)
}return 0;
}
關於曼哈頓距離下的最小生成樹
這些天一直在集訓,考了十幾次 zzy出了一道曼哈頓距離下的最小生成樹,考場上我沒做出來 嗯 這種題目的問題在於,你沒辦法把每兩個點都建一條邊 但是因為是曼哈頓距離,所以有一些特殊性質 容易證明,將某個點為原點建立笛卡爾座標系,將座標系分為每45 角為一塊的八個區域 那麼這個點向每個區域只會朝其中的某...
二維曼哈頓距離最小生成樹
原理 每個點在以它為頂點的45 角範圍內,只可能連向距離 曼哈頓距離 它最近的點。證明 以點a為原點,y軸正半軸向x軸正半軸方向偏45 角為例 如圖所示,設 ab ac 所有距離都是曼哈頓距離 a 0,0 b x1,y1 c x2,y2 ab x1 y1 ac x2 y2 bc x2 x1 y2 y...
曼哈頓距離最小生成樹與莫隊演算法
一 曼哈頓距離最小生成樹 曼哈頓距離最小生成樹問題可以簡述如下 給定二維平面上的n個點,在兩點之間連邊的代價為其曼哈頓距離,求使所有點連通的最小代價。樸素的演算法可以用o n2 的prim,或者處理出所有邊做kruskal,但在這裡總邊數有o n2 條,所以kruskal的複雜度變成了o n2log...