1.7 光影切割問題
不少人很愛玩遊戲,例如 cs ⑨。 遊戲設計也成為程式開發的熱點之一,
我們假設要設計破舊倉庫之類的場景作為戰爭遊戲的背景。倉庫的地面會因為陽光從屋頂的漏洞或者視窗照射進來而形成許多光照區域和陰影區域。
為了簡單起見,假設不同區域的邊界都是直線 ⑩, 我們把這些直線都叫做「光影線」,並且不存在三條光影線相交於一點的情況。
那麼,如果我們需要快速計算某個時刻,在 x 座標[ a, b] 區間的地板上被光影劃分成多少塊。如何才能寫出演算法來計算呢?
1、首先觀察2條直線最多把區間分成4個部分。
2、對於三條直線,若三條直線兩個交點分成6個部分,若三個交點分成7個部分;
3、如下規律:
2條直線--> 1個交點 --> 空間分成4個部分
3條直線--> 2個交點 --> 空間分成6個部分
3條直線--> 3個交點 --> 空間分成7個部分
4、每增加一條直線,增加m個交點,那麼這條直線被新增加的m個交點分成m+1段,每條直線把原來區域分成2塊,
因此,新增加m+1塊新區域;所以n條直線,m個交點,共n+m+1;
因此該問題就轉化為求直線交點個數的問題。
更為巧妙,進一步將問題轉化為求逆序數問題。但書中沒有給出如何求解逆序數。
逆序數題目參考:
hdoj 3465 逆序數
line都是直線,不是線段,沒有端點,輸入的點只是用於確定這條直線的位置
對每條直線與x=l,和x=r的交點分別為xl,xr,兩直線在(l,r)相交,必然有
xl1>xl2&&xr1xr2,(a.l-b.l)*(a.r-b.r)<0
如果將與l的交點從小到大排,那麼當相應的r的交點出現乙個逆序數對就表示有兩直線有交點.
先將所有直線根據l遞增排序,之後編號1~n,再根據r遞減排序,得到乙個編號序列。
例如3412,遞減,其中r3>r4>r1>r2, 又編號:l1所以12 34 所以3和4有交點,1和2有交點。
這符合逆序數的關係:乙個數的逆序數是在它之前比他大的數的個數,
當然這裡是小的數,原因是為了方便樹狀陣列處理。所以只要根據上述方法排序再求逆序數即可。
1.與y軸平行得線,只要這樣的線在(l,r)範圍內,則必定跟別的不平行線相交。
所以計算下個數在乘積
2.l和r相同的情況。當l相同時,r遞增排序;當r相同時,l遞減排序。
就能使在l和r上的交點不計算在內。
參考別人的樹狀陣列
樹狀陣列可以說是線段樹的簡化,處理單一,但寫起來簡便。
兩個函式:
add(x,val),時c[x]+=val;
sum(x),求c[1]+c[2]+...+c[x];
#include#include#includeusing namespace std;
#define lowbit(x) (x&(-x))
const int n=5e4+10;
struct node;
node e[n];
int c[n];
double l,r;
int cmp1(node x,node y) //left 遞增排序
void add(int x,int val)
return ans;
} int main()
k=(y2-y1)/(x2-x1);//y=kx+b
b=y1-k*x1;
e[t].a=l*k+b;
e[t++].b=r*k+b;
} sort(e,e+t,cmp1);
for(i=0;ie[i].num=i+1;
sort(e,e+t,cmp2);//遞減排序 3412
memset(c,0,sizeof(c));
for(i=0;iprintf("%d\n",ans+tt*t);//加上平行y軸的直線所產生的交點
} }
小飛的電梯排程演算法,光影切割問題《程式設計之美》
小飛的電梯排程演算法 1.8問題描述 小飛是實習程式設計師,下班高峰期時電梯的每層都有人上下,等電梯是件煩人的事情。小飛有個方案 電梯每次計算裡面的人,和上的樓層,從而統計出在哪一層停,該上樓的出電梯爬樓梯 該下樓的出電梯爬樓梯下去。問題 電梯停靠在哪一層,能保證這次乘客爬樓梯的層數總和最少?分析 ...
重新開始戰鬥05 程式設計之美 光影切割
問題描述 假設有乙個矩形區域,有若干條直線切割該區域,並且沒有一條直線與y軸平行,且不存在三條 以及3條以上 切割線相交於一點的情況。請問該矩形平面被分割成多少塊。分析 假設一塊矩形區域已經被切割成很多塊,那麼此時再增加一條切割線,新的切割線與其他切割線相交,且有m個交點。那麼新的切割線被分割成m ...
光影切割問題
不少人很愛玩遊戲,例如 cs 遊戲設計也成為程式開發的熱點之一,我們假設要設計破舊倉庫之類的場景作為戰爭遊戲的背景。倉庫的地面會因為陽光從屋頂的漏洞或者視窗照射進來而形成許多光照區域和陰影區域。為了簡單起見,假設不同區域的邊界都是直線 我們把這些直線都叫做 光影線 並且不存在三條光影線相交於一點的情...