巨人和鬼
一組n個巨人正與n個鬼進行戰鬥,每個巨人的**是乙個質子炮, 它可以把一串質子流射中鬼而把鬼消滅。質子流沿直線行進,在擊中鬼時就終止。巨人決定採取下述策略。他們尋找鬼配對,以形成n個巨人─鬼對,。然後每個巨人同時向他選取的鬼射出一串質子流。我們知道,讓質子流互相交叉是很危險的。因此巨人選擇的配對方式應該使質子流都不會交叉。假定每個巨人和每個鬼的位置都是平面上的乙個固定點,並且沒有三個位置共線, 求一種配對方案。
自己分析:採用分治方法,尋找中間界限,將大區間問題分成左右2個子區間,並同理遞迴求解。可能有很多種配對的方法,但只需找一種即可。此種方法必能找一種,因為每次找的都是成立的,能保證左右兩邊都能找到配對的。
演算法分析:
我們設p1..pn為巨人的固定點;pn+1..p2n為鬼的固定點。我們採取分治採取分治策略尋找序列[pp..pr]中的配對方案(初始時[pp..pr]為[p1..p2n]):
在[pp..pr]中找出乙個最低位置(y座標值最小)的乙個點p0,如果這樣的點有多個,則選取最左邊的點為p0,p0與pp交換。然後將其餘點[pp+1..pr]按相對 pp的極角遞增的順序排列。顯然pp與其餘點pp+1..pr之間的任何線段是不會交叉的。我們從pp開始尋找乙個巨人和鬼成對的最小子區間[pp..pi](p≤i≤r)。若該子區間僅剩乙個元素,配對結束;否則巨人(鬼)pp與鬼(巨人)pi配對。這樣使得尚未配對的巨人和鬼分布在兩個子區間[pp+1..pi-1],[pi+1..pr]。繼續按上述分治策略分別遞迴求解[pp+1..pi-1]和[pi+1..pr]。
如上圖,以點p1,將其他點按相對p1的極角遞增排序。然後從p2開始順序地找乙個最短的配對序列p1-p6(鬼和巨人的個數要相等,這樣才能一一配對。p1-p6是3個鬼,3個巨人)。怎樣求分割線p1p6呢?是給鬼和巨人乙個標誌,設鬼為-1,巨人為1,從p2開始找時,逐漸累加,直到為1時停止,表明鬼和人的個數相等,如p1到p2時:2個鬼(-1-1=-2),繼續到p3:2鬼1巨人(-1-1+1=-1),p4:3鬼1巨人(-1-1+1-1=-2),p5:3鬼2巨人(-1-1+1-1+1=-1),p6:3鬼3巨人(-1-1+1-1+1+1=0),此時鬼和巨人的個數相等,則分割線為p1p6,將p1-p8分割成(p2-p5)和(p7-p8)。再遞迴對(p2-p5)和(p7-p8)按同樣的方法分治求解。
m = list[p].k; i = p;//其中p為區間[p,r]的起點,鬼的k=-1,巨人的k=1.m為巨人、鬼個數累積和
while ( m != 0)//當m=0時,表明找到了乙個最短的鬼和巨人個數相等(即可配對)的子區間
x, y : real
end;
varn, i : integer;
f : text;
list, lt : array [1..2 * maxn] of node;
p0 : node;
function comp(var p1, p2 : node):boolean;
begin
if (p1.x-p0.x)*(p2.y-p0.y) - (p1.y-p0.y)*(p2.x-p0.x) > 0
then comp := true
else comp := false
end;
procedure merge(p, q, r : integer);
vari, j, t : integer;
begin
t := p; i := p; j := q + 1;
while t <= r do begin
if (i <= q) and ((j > r) or comp(list[i], list[j])) then
begin
lt[t] := list[i]; inc(i)
endelse begin
lt[t] := list[j]; inc(j)
end;
inc(t)
end;
for i := p to r do list[i] := lt[i]
end;
procedure merge_sort(p, r : integer);
varq : integer;
begin
if p <> r then begin
q := (p + r - 1) div 2;
merge_sort(p, q);
merge_sort(q + 1, r);
merge(p, q, r)
endend;
procedure swap(var a, b : node);
vart : node;
begin
t := a; a := b; b := t
end;
procedure out_pos(var p : node);
begin
write(p.x:8:2, p.y:8:2)
end;
procedure pick(p, r : integer);
vari, m : integer;
begin
if p < r then begin
m := p;
for i := p to r do
if (list[i].y < list[m].y) or (list[i].y = list[m].y)
and (list[i].x < list[m].x) then m := i;
swap(list[p], list[m]);
p0 := list[p];
merge_sort(p + 1, r);
m := list[p].k; i := p;
repeat
inc(i);
m := m + list[i].k
until m = 0;
out_pos(list[p]);
out_pos(list[i]);
writeln;
pick(p+1, i - 1);
pick(i + 1, r)
endend;
begin
assign(f, 'input.dat');
reset(f);
readln(f, n);
for i := 1 to n do begin
readln(f, list[i].x, list[i].y);
list[i].k := 1
end;
for i := n + 1 to 2 * n do begin
readln(f, list[i].x, list[i].y);
list[i].k := -1
end;
pick(1, 2 * n)
end.
分治 遞迴實現 巨人與鬼
巨人和鬼 一組n個巨人正與n個鬼進行戰鬥,每個巨人的 是乙個質子炮,它可以把一串質子流射中鬼而把鬼消滅。質子流沿直線行進,在擊中鬼時就終止。巨人決定採取下述策略。他們尋找鬼配對,以形成n個巨人 鬼對,然後每個巨人同時向他選取的鬼射出一串質子流。我們知道,讓質子流互相交叉是很危險的。因此巨人選擇的配對...
分治法 凸包問題
什麼是凸包?我的理解就是,圖形任意兩點的連線都沒有在圖形外部。問題 給定點集,怎麼求出凸包的邊界點呢?第一步 給這些點按照x的從大到小進行排序,如果x相同的按照y再排序。第二步 把x最小的和最大的連起來,他們必為凸包的邊界點。第三步 把平面區域分為兩個部分,分別在上面和下面去找面積最大的三角形 面積...
凸包問題之分治法
凸包 按橫座標排序,以最小點與最大點之間的連線為準,在直線一側找使三角形面積最大的點,此點必在凸包內,以找到點與最大點或最小點繼續遞迴以尋找最大三角形面積尋找凸包點,直至找不到符合條件的點。實現 如下 include include include define max size 10001 str...