
2010年10月13日
一、數論算法
1.求兩數的最大公約數
2.求兩數的最小公倍數
3.素數的求法
A.小范圍內判斷一個數是否為質數:
B.判斷longint范圍內的數是否為素數(包含求50000以內的素數表):
二、圖論算法
1.最小生成樹
A.Prim算法:
B.Kruskal算法:(貪心)
按權值遞增順序刪去圖中的邊,若不形成回路則將此邊加入最小生成樹。
2.最短路徑
A.標號法求解單源點最短路徑:
B.Floyed算法求解所有頂點對之間的最短路徑:
C. Dijkstra 算法:
3.計算圖的傳遞閉包
4.無向圖的連通分量
A.深度優先
B 寬度優先(種子染色法)
5.關鍵路徑
幾個定義: 頂點1為源點,n為匯點。
a. 頂點事件最早發生時間Ve[j], Ve [j] = max{ Ve [j] + w[I,j] },其中Ve (1) = 0;
b. 頂點事件最晚發生時間 Vl[j], Vl [j] = min{ Vl[j] – w[I,j] },其中 Vl(n) = Ve(n);
c. 邊活動最早開始時間 Ee[I], 若邊I由<j,k>表示,則Ee[I] = Ve[j];
d. 邊活動最晚開始時間 El[I], 若邊I由<j,k>表示,則El[I] = Vl[k] – w[j,k];
若 Ee[j] = El[j] ,則活動j為關鍵活動,由關鍵活動組成的路徑為關鍵路徑。
求解方法:
a. 從源點起topsort,判斷是否有回路并計算Ve;
b. 從匯點起topsort,求Vl;
c. 算Ee 和 El;
6.拓撲排序
找入度為0的點,刪去與其相連的所有邊,不斷重復這一過程。
例 尋找一數列,其中任意連續p項之和為正,任意q 項之和為負,若不存在則輸出NO.
7.回路問題
Euler回路(DFS)
定義:經過圖的每條邊僅一次的回路。(充要條件:圖連同且無奇點)
Hamilton回路
定義:經過圖的每個頂點僅一次的回路。
一筆畫
充要條件:圖連通且奇點個數為0個或2個。
9.判斷圖中是否有負權回路 Bellman-ford 算法
x[I],y[I],t[I]分別表示第I條邊的起點,終點和權。共n個結點和m條邊。
10.第n最短路徑問題
*第二最短路徑:每舉最短路徑上的每條邊,每次刪除一條,然后求新圖的最短路徑,取這些路徑中最短的一條即為第二最短路徑。
*同理,第n最短路徑可在求解第n-1最短路徑的基礎上求解。
三、背包問題
*部分背包問題可有貪心法求解:計算Pi/Wi
數據結構:
w[i]:第i個背包的重量;
p[i]:第i個背包的價值;
1.0-1背包: 每個背包只能使用一次或有限次(可轉化為一次):
A.求最多可放入的重量。
B.求可以放入的最大價值。
F[I,j] 為容量為I時取前j個背包所能獲得的最大價值。
F [i,j] = max { f [ i – w [ j ], j-1] + p [ j ], f[ i,j-1] }
C.求恰好裝滿的情況數。
2.可重復背包
A求最多可放入的重量。
F[I,j]為前i個物品中選擇若干個放入使其體積正好為j的標志,為布爾型。
狀態轉移方程為
f[I,j] = f [ I-1, j – w[I]*k ] (k=1.. j div w[I])
B.求可以放入的最大價值。
f[i,j] = max { f [i- k*w[j], j-1] + k*p[j] } (0<=k<= i div w[j])
其中f[i,j]表示容量為i時取前j種背包所能達到的最大值。
C.求恰好裝滿的情況數。
Ahoi2001 Problem2
求自然數n本質不同的質數和的表達式的數目。
思路一,生成每個質數的系數的排列,在一一測試,這是通法。
思路二,遞歸搜索效率較高
思路三:可使用動態規劃求解
四、排序算法
1.快速排序:
B.插入排序:
思路:當前a[1]..a[i-1]已排好序了,現要插入a[i]使a[1]..a[i]有序。
C.選擇排序:
D. 冒泡排序
E.堆排序:
F. 歸并排序
G.基數排序
思想:對每個元素按從低位到高位對每一位進行一次排序
五、高精度計算
高精度數的定義:
1.高精度加法
2.高精度減法
3.高精度乘以低精度
4.高精度乘以高精度
5.高精度除以低精度
6.高精度除以高精度
六、 樹的遍歷
1.已知前序中序求后序
2.已知中序后序求前序
3.已知前序后序求中序的一種
七 進制轉換
1任意正整數進制間的互化
除n取余
2實數任意正整數進制間的互化
乘n取整
3負數進制:
設計一個程序,讀入一個十進制數的基數和一個負進制數的基數,并將此十進制數轉換為此負 進制下的數:-R∈{-2,-3,-4,....-20}
八 全排列與組合的生成
1排列的生成:(1..n)
2組合的生成(1..n中選取k個數的所有方案)
九.查找算法
1折半查找
2樹形查找
二叉排序樹:每個結點的值都大于其左子樹任一結點的值而小于其右子樹任一結點的值。
查找
十、貪心
*會議問題
(1) n個活動每個活動有一個開始時間和一個結束時間,任一時刻僅一項活動進行,求滿足活動數最多的情況。
解:按每項活動的結束時間進行排序,排在前面的優先滿足。
(2)會議室空閑時間最少。
(3)每個客戶有一個愿付的租金,求最大利潤。
(4)共R間會議室,第i個客戶需使用i間會議室,費用相同,求最大利潤。
十一、回溯法框架
1. n皇后問題
2.Hanoi Tower h(n)=2*h(n-1)+1 h(1)=1
十二、DFS框架
十三、BFS框架
十五、數據結構相關算法
1.鏈表的定位函數
2.單鏈表的插入操作
3.單鏈表的刪除操作
4.雙鏈表的插入操作(插入新結點q)
5.雙鏈表的刪除操作
原文鏈接:http://old.blog.edu.cn/user3/Hailer/archives/2006/1545396.shtml
posted @
2010-10-13 09:46 孟起 閱讀(1914) |
評論 (0) |
編輯 收藏

2010年10月12日
摘要: FOJ
Hotter Colder
http://acm.fzu.edu.cn/problem.php?pid=1014
求線段的中位線,線段相交求交點,求凸多邊形的面積,
無歸之室
http://acm.fzu.edu.cn/problem.php?pid=1016
本題精度要求非常高,用三角函數的話,很容易就wa..
Reflections
http://acm.fzu.e...
閱讀全文
posted @
2010-10-12 17:36 孟起 閱讀(689) |
評論 (0) |
編輯 收藏
Euler的任意四面體體積公式(已知邊長求體積)


已知4點坐標求體積(其中四個點的坐標分別為(x1,y1,z1),(x2,y2,z2),(x3,y3,z3),(x4,y4,z4)

注意事項:
1. 注意舍入方式(0.5的舍入方向);防止輸出-0.
2. 幾何題注意多測試不對稱數據.
3. 整數幾何注意xmult和dmult是否會出界;
符點幾何注意eps的使用.
4. 避免使用斜率;注意除數是否會為0.
5. 公式一定要化簡后再代入.
6. 判斷同一個2*PI域內兩角度差應該是
abs(a1-a2)<beta||abs(a1-a2)>pi+pi-beta;
相等應該是
abs(a1-a2)<eps||abs(a1-a2)>pi+pi-eps;
7. 需要的話盡量使用atan2,注意:atan2(0,0)=0,
atan2(1,0)=pi/2,atan2(-1,0)=-pi/2,atan2(0,1)=0,atan2(0,-1)=pi.
8. cross product = |u|*|v|*sin(a)
dot product = |u|*|v|*cos(a)
9. (P1-P0)x(P2-P0)結果的意義:
正: <P0,P1>在<P0,P2>順時針(0,pi)內
負: <P0,P1>在<P0,P2>逆時針(0,pi)內
0 : <P0,P1>,<P0,P2>共線,夾角為0或pi
posted @
2010-10-12 12:00 孟起 閱讀(7123) |
評論 (0) |
編輯 收藏
原文鏈接:
http://cschf.spaces.live.com/blog/cns!E113B8D05D833E2B!140.entry寫幾點不熟悉的
12. 判斷點是否在多邊形中
13. 判斷線段是否在多邊形內
25. 計算線段或直線與線段的交點
27. 求線段或直線與圓的交點
判斷點是否在多邊形中:
判斷點P是否在多邊形中是計算幾何中一個非常基本但是十分重要的算法。以點P為端點,向左方作射線L,由于多邊形是有界的,所以射線L的左端一定在多邊形外,考慮沿著L從無窮遠處開始自左向右移動,遇到和多邊形的第一個交點的時候,進入到了多邊形的內部,遇到第二個交點的時候,離開了多邊形,……所以很容易看出當L和多邊形的交點數目C是奇數的時候,P在多邊形內,是偶數的話P在多邊形外。
但是有些特殊情況要加以考慮。如圖下圖(a)(b)(c)(d)所示。在圖(a)中,L和多邊形的頂點相交,這時候交點只能計算一個;在圖(b)中,L和多邊形頂點的交點不應被計算;在圖(c)和(d) 中,L和多邊形的一條邊重合,這條邊應該被忽略不計。如果L和多邊形的一條邊重合,這條邊應該被忽略不計。
為了統一起見,我們在計算射線L和多邊形的交點的時候,1。對于多邊形的水平邊不作考慮;2。對于多邊形的頂點和L相交的情況,如果該頂點是其所屬的邊上縱坐標較大的頂點,則計數,否則忽略;3。對于P在多邊形邊上的情形,直接可判斷P屬于多邊行。由此得出算法的偽代碼如下:
count ← 0;
以P為端點,作從右向左的射線L;
for 多邊形的每條邊s
do if P在邊s上
then return true;
if s不是水平的
then if s的一個端點在L上
if 該端點是s兩端點中縱坐標較大的端點
then count ← count+1
else if s和L相交
then count ← count+1;
if count mod 2 = 1
then return true;
else return false;
其中做射線L的方法是:設P'的縱坐標和P相同,橫坐標為正無窮大(很大的一個正數),則P和P'就確定了射線L。
判斷點是否在多邊形中的這個算法的時間復雜度為O(n)。
另外還有一種算法是用帶符號的三角形面積之和與多邊形面積進行比較,這種算法由于使用浮點數運算所以會帶來一定誤差,不推薦大家使用。
判斷線段是否在多邊形內:
線段在多邊形內的一個必要條件是線段的兩個端點都在多邊形內,但由于多邊形可能為凹,所以這不能成為判斷的充分條件。如果線段和多邊形的某條邊內交(兩線段內交是指兩線段相交且交點不在兩線段的端點),因為多邊形的邊的左右兩側分屬多邊形內外不同部分,所以線段一定會有一部分在多邊形外(見圖a)。于是我們得到線段在多邊形內的第二個必要條件:線段和多邊形的所有邊都不內交。
線段和多邊形交于線段的兩端點并不會影響線段是否在多邊形內;但是如果多邊形的某個頂點和線段相交,還必須判斷兩相鄰交點之間的線段是否包含于多邊形內部(反例見圖b)。
因此我們可以先求出所有和線段相交的多邊形的頂點,然后按照X-Y坐標排序(X坐標小的排在前面,對于X坐標相同的點,Y坐標小的排在前面,這種排序準則也是為了保證水平和垂直情況的判斷正確),這樣相鄰的兩個點就是在線段上相鄰的兩交點,如果任意相鄰兩點的中點也在多邊形內,則該線段一定在多邊形內。
證明如下:
命題1:
如果線段和多邊形的兩相鄰交點P1 ,P2的中點P' 也在多邊形內,則P1, P2之間的所有點都在多邊形內。
證明:
假設P1,P2之間含有不在多邊形內的點,不妨設該點為Q,在P1, P'之間,因為多邊形是閉合曲線,所以其內外部之間有界,而P1屬于多邊行內部,Q屬于多邊性外部,P'屬于多邊性內部,P1-Q-P'完全連續,所以P1Q和QP'一定跨越多邊形的邊界,因此在P1,P'之間至少還有兩個該線段和多邊形的交點,這和P1P2是相鄰兩交點矛盾,故命題成立。證畢。
由命題1直接可得出推論:
推論2:
設多邊形和線段PQ的交點依次為P1,P2,……Pn,其中Pi和Pi+1是相鄰兩交點,線段PQ在多邊形內的充要條件是:P,Q在多邊形內且對于i =1, 2,……, n-1,Pi ,Pi+1的中點也在多邊形內。
在實際編程中,沒有必要計算所有的交點,首先應判斷線段和多邊形的邊是否內交,倘若線段和多邊形的某條邊內交則線段一定在多邊形外;如果線段和多邊形的每一條邊都不內交,則線段和多邊形的交點一定是線段的端點或者多邊形的頂點,只要判斷點是否在線段上就可以了。
至此我們得出算法如下:
if 線端PQ的端點不都在多邊形內
then return false;
點集pointSet初始化為空;
for 多邊形的每條邊s
do if 線段的某個端點在s上
then 將該端點加入pointSet;
else if s的某個端點在線段PQ上
then 將該端點加入pointSet;
else if s和線段PQ相交 // 這時候已經可以肯定是內交了
then return false;
將pointSet中的點按照X-Y坐標排序;
for pointSet中每兩個相鄰點 pointSet[i] , pointSet[ i+1]
do if pointSet[i] , pointSet[ i+1] 的中點不在多邊形中
then return false;
return true;
這個過程中的排序因為交點數目肯定遠小于多邊形的頂點數目n,所以最多是常數級的復雜度,幾乎可以忽略不計。因此算法的時間復雜度也是O(n)。
計算線段或直線與線段的交點:
設一條線段為L0 = P1P2,另一條線段或直線為L1 = Q1Q2 ,要計算的就是L0和L1的交點。
1. 首先判斷L0和L1是否相交(方法已在前文討論過),如果不相交則沒有交點,否則說明L0和L1一定有交點,下面就將L0和L1都看作直線來考慮。
2. 如果P1和P2橫坐標相同,即L0平行于Y軸
a) 若L1也平行于Y軸,
i. 若P1的縱坐標和Q1的縱坐標相同,說明L0和L1共線,假如L1是直線的話他們有無窮的交點,假如L1是線段的話可用"計算兩條共線線段的交點"的算法求他們的交點(該方法在前文已討論過);
ii. 否則說明L0和L1平行,他們沒有交點;
b) 若L1不平行于Y軸,則交點橫坐標為P1的橫坐標,代入到L1的直線方程中可以計算出交點縱坐標;
3. 如果P1和P2橫坐標不同,但是Q1和Q2橫坐標相同,即L1平行于Y軸,則交點橫坐標為Q1的橫坐標,代入到L0的直線方程中可以計算出交點縱坐標;
4. 如果P1和P2縱坐標相同,即L0平行于X軸
a) 若L1也平行于X軸,
i. 若P1的橫坐標和Q1的橫坐標相同,說明L0和L1共線,假如L1是直線的話他們有無窮的交點,假如L1是線段的話可用"計算兩條共線線段的交點"的算法求他們的交點(該方法在前文已討論過);
ii. 否則說明L0和L1平行,他們沒有交點;
b) 若L1不平行于X軸,則交點縱坐標為P1的縱坐標,代入到L1的直線方程中可以計算出交點橫坐標;
5. 如果P1和P2縱坐標不同,但是Q1和Q2縱坐標相同,即L1平行于X軸,則交點縱坐標為Q1的縱坐標,代入到L0的直線方程中可以計算出交點橫坐標;
6. 剩下的情況就是L1和L0的斜率均存在且不為0的情況
a) 計算出L0的斜率K0,L1的斜率K1 ;
b) 如果K1 = K2
i. 如果Q1在L0上,則說明L0和L1共線,假如L1是直線的話有無窮交點,假如L1是線段的話可用"計算兩條共線線段的交點"的算法求他們的交點(該方法在前文已討論過);
ii. 如果Q1不在L0上,則說明L0和L1平行,他們沒有交點。
c) 聯立兩直線的方程組可以解出交點來
這個算法并不復雜,但是要分情況討論清楚,尤其是當兩條線段共線的情況需要單獨考慮,所以在前文將求兩條共線線段的算法單獨寫出來。另外,一開始就先利用矢量叉乘判斷線段與線段(或直線)是否相交,如果結果是相交,那么在后面就可以將線段全部看作直線來考慮。需要注意的是,我們可以將直線或線段方程改寫為ax+by+c=0的形式,這樣一來上述過程的部分步驟可以合并,縮短了代碼長度,但是由于先要求出參數,這種算法將花費更多的時間。
求線段或直線與圓的交點:
設圓心為O,圓半徑為r,直線(或線段)L上的兩點為P1,P2。
1. 如果L是線段且P1,P2都包含在圓O內,則沒有交點;否則進行下一步。
2. 如果L平行于Y軸,
a) 計算圓心到L的距離dis;
b) 如果dis > r 則L和圓沒有交點;
c) 利用勾股定理,可以求出兩交點坐標,但要注意考慮L和圓的相切情況。
3. 如果L平行于X軸,做法與L平行于Y軸的情況類似;
4. 如果L既不平行X軸也不平行Y軸,可以求出L的斜率K,然后列出L的點斜式方程,和圓方程聯立即可求解出L和圓的兩個交點;
5. 如果L是線段,對于2,3,4中求出的交點還要分別判斷是否屬于該線段的范圍內。
posted @
2010-10-12 10:18 孟起 閱讀(699) |
評論 (0) |
編輯 收藏
一、點的基本運算
1. 平面上兩點之間距離 1
2. 判斷兩點是否重合 1
3. 矢量叉乘 1
4. 矢量點乘 2
5. 判斷點是否在線段上 2
6. 求一點饒某點旋轉后的坐標 2
7. 求矢量夾角 2
二、 線段及直線的基本運算
1. 點與線段的關系 3
2. 求點到線段所在直線垂線的垂足 4
3. 點到線段的最近點 4
4. 點到線段所在直線的距離 4
5. 點到折線集的最近距離 4
6. 判斷圓是否在多邊形內 5
7. 求矢量夾角余弦 5
8. 求線段之間的夾角 5
9. 判斷線段是否相交 6
10.判斷線段是否相交但不交在端點處(內交) 6
11.求線段所在直線的方程 6
12.求直線的斜率 7
13.求直線的傾斜角 7
14.求點關于某直線的對稱點 7
15.判斷兩條直線是否相交及求直線交點 7
16.判斷線段是否相交,如果相交返回交點 7
三、多邊形常用算法模塊
1. 判斷多邊形是否簡單多邊形 8
2. 檢查多邊形頂點的凸凹性 9
3. 判斷多邊形是否凸多邊形 9
4. 求多邊形面積 9
5. 判斷多邊形頂點的排列方向,方法一 10
6. 判斷多邊形頂點的排列方向,方法二 10
7. 射線法判斷點是否在多邊形內 10
8. 判斷點是否在凸多邊形內 11
9. 尋找點集的graham算法 12
10.尋找點集凸包的卷包裹法 13
11.判斷線段是否在多邊形內 14
12.求簡單多邊形的重心 (HDU1115)15
13.求凸多邊形的重心 17
14.求肯定在給定多邊形內的一個點 17
15.求從多邊形外一點出發到該多邊形的切線 18
16.判斷多邊形的核是否存在 19
四、 圓的基本運算
1 .點是否在圓內 20
2 .求不共線的三點所確定的圓 21
五、矩形的基本運算
1.已知矩形三點坐標,求第4點坐標 22
六、常用算法的描述 22
七、補充
1.兩圓關系: 24
2.判斷圓是否在矩形內: 24
3.點到平面的距離: 25
4.點是否在直線同側: 25
5.鏡面反射線: 25
6.矩形包含: 26
7.兩圓交點: 27
8.兩圓公共面積: 28
9. 圓和直線關系: 29
10. 內切圓: 30
11. 求切點: 31
12. 線段的左右旋: 31
13.公式: 32
附上一篇博客:計算幾何算法概覽
zoj上的計算幾何題
Vol I
1010 by pandahyx
1032 by javaman
1037 by Vegetable Bird
1041 by javaman
1081 by Vegetable Bird
1090 by Vegetable Bird
Vol II
1104 by javaman
1123 by javaman
1139 by Vegetable Bird
1165 by javaman
1199 by Vegetable Bird
Vol V
1426 by Vegetable Bird
1439 by Vegetable Bird
1460 by Vegetable Bird
1472 by Vegetable Bird
Vol VI
1597 by Vegetable Bird
Vol VII
1608 by Vegetable Bird
1648 by Vegetable Bird
Vol XII
2102 by pandahyx
2107 by pandahyx
2157 by pandahyx
Vol XIII
2234 by pandahyx
Vol XIV
2318 by ahyangyi
2394 by qherlyt
Vol XV
2403 by Vegetable Bird
posted @
2010-10-12 09:52 孟起 閱讀(575) |
評論 (0) |
編輯 收藏
1. 一種是用矢量叉乘法:
由三個頂點向所求的點引出矢量(注意方向),然后任意用其中兩個矢量形成平面,再用這個平面和剩下的矢量叉乘,得出一個新矢量,方向向里,則在三角形外,反之在里面。
2.用面積方法
#include<stdio.h>
#include<math.h>

struct TPoint
{
float x;
float y;
};

//求叉積

float mul(struct TPoint p1, struct TPoint p2, struct TPoint p0)
{
return ((p1.x - p0.x)*(p2.y - p0.y)-(p2.x - p0.x)*(p1.y - p0.y));
}

/**//*由三個頂點向所求的點引出矢量(注意方向),然后任意用其中兩個矢量形成平面,
* 再用這個平面和剩下的矢量叉乘,得出一個新矢量,方向向里,則在三角形外,反之在里面。
*/

int inside(struct TPoint tr[], struct TPoint p)
{
int i;
for (i = 0; i < 3; i++)
if (mul(p, tr[i], tr[(i + 1) % 3]) * mul(p, tr[(i + 2) % 3], tr[(i + 1) % 3]) > 0)
return 0;
return 1;
}


float area(struct TPoint p1, struct TPoint p2, struct TPoint p3)
{
return fabs((p1.x - p3.x)*(p2.y - p3.y)-(p2.x - p3.x)*(p1.y - p3.y));
}
//用面積判斷p是否在三角形內

int inside2(struct TPoint tr[], struct TPoint p)
{
if (fabs(area(tr[0], tr[1], tr[2]) -
area(p, tr[1], tr[2]) -
area(tr[0], p, tr[2]) -
area(tr[0], tr[1], p)) < 1.0e-20)
return 1;
else
return 0;
}


int main()
{

struct TPoint tr[3] =
{
{-1, 1},
{1, 0},
{3, 0}}, p =
{1, 2};

//方法一
printf("algorithm 1:");
if (inside(tr, p))
printf("In\n");
else
printf("Out\n");

//方法一
printf("algorithm 2:");
if (inside2(tr, p))
printf("In\n");
else
printf("Out\n");
}
posted @
2010-10-12 09:40 孟起 閱讀(1866) |
評論 (0) |
編輯 收藏

2010年10月11日
題目:一共來了n(0<n<25)個同學,按照組隊規則(自由組隊,每隊人數不限),一共會有多少種不同的組隊方案呢?
遞推式是:a[i][j]=a[i-1][j-1]+a[i-1][j]*j;
而且。a[i][0]應該是為0,不為1的。
此外還得注溢出。要用__int64類型。
http://acm.hdu.edu.cn/showproblem.php?pid=1292
#include<stdio.h>
int main() {
int t, n, i, j;
__int64 a[26][26];
a[1][1] = 1;
a[1][0] = 0;
for (i = 2; i <= 25; i++) {
a[i][0] = 0;
a[i][i] = 1;
for (j = 1; j < i; j++)
a[i][j] = a[i - 1][j - 1] + a[i - 1][j] * j;
}
scanf("%d", &t);
while (t--) {
scanf("%d", &n);
__int64 sum = 1;
for (i = 2; i <= n; i++)
sum += a[n][i];
printf("%I64d\n", sum);
}
return 0;
}
posted @
2010-10-11 20:03 孟起 閱讀(574) |
評論 (0) |
編輯 收藏
在ICPC比賽中,個人能力方面,如果粗略地分的話,大致可以分為算法能力、代碼能力和查錯能力。那些大學才開始參加比賽的選手,寫代碼的基本功一般會比較扎實,主要瓶頸應該是算法能力。而對于OI轉ICPC的選手來說,代碼能力往往是最大的缺陷。隨著OI轉ICPC的選手逐漸增多,代碼能力的問題愈發暴露了出來。
一、如何定義代碼能力
Comars曾經給代碼能力作過一個比較準確的定義。2004年暑假時,Comars曾經說過:他認為150行以內的題目,他的1Y率非常高,并且保持穩定;而當代碼長度超過150行以后,1Y率就開始急速下降了。如果我們畫出一條1Y率的曲線的話,150行就是一個轉折點。我們不妨認為,150行就是Comars當時的代碼能力。一年以后,經過努力,Comars把代碼能力提高到了250行。不過,這已經是后話了。
二、如何提高代碼能力
我一直覺得寫程序和寫文章是一個對很好的類比。
寫文章需要先從宏觀入手,構思文章的結構。寫程序同樣需要。一個好的結構,就是一個好的開始。一個好的開始,是成功的一半。
一篇好的文章需要各種句式和詞藻的合理組合。體現到寫程序上來,就是一些單句以及三五行的小結構的熟練使用。這些都是需要平時總結和積累的。
但凡文章寫得好的人,一定看過很多別人寫的文章。同樣的道理,多看別人的程序,用心地去看,也可以提高自己的代碼能力。
我鼓勵隊員去看別人寫的程序,特別是像Comars這樣的選手寫的程序。從優秀的程序中,我們可以體會別人良好的程序結構,同時也可以學到很多寫程序的技巧――三五行的小技巧。在和Comars做隊友的兩年時間里,我通過看Comars的程序,學會了很多小技巧。逐漸地,我覺得我寫的某些程序已經和Comars有點相像了。
那么,如果身邊沒有Comars這樣優秀的選手可以借鑒,該怎么辦呢?其實沒關系。任何一個程序都是可以看的。一個程序,就算寫得再差,總還會有一兩個閃光點,要想辦法把它們找出來。另外,程序里寫得不好的地方,也要一一找出來。
讀程序,從某種角度來看,就像讀史。好的歷史是用來借鑒的;不好的歷史則應該引以為戒。讀程序也是一樣,擇其善者而從之,其不善者而改之。
三、謹慎地對待STL和SCL
STL - Standard Template Library。在ICPC的選手中,STL是相當受歡迎的。的確,如果STL用得好,程序可以精簡很多。既提高了編程的速度,也提高了編程的準確性。
SCL - Standard Code Library,就是標準程序庫。對很多選手來說,SCL可是命根子啊
我覺得STL和SCL都不是壞東西,但是需要謹慎地使用。
我向來不主張隊員一進隊就開始用STL(雖然這種現象普遍存在 )。我認為,STL的作用是錦上添花,而不是雪中送炭。比方說,一個heap寫得很熟練的隊員,我覺得他可以偷偷懶,用一下STL。但是,那些不太會寫heap的隊員,就不應該用STL里的heap。因為,他們真正應該做的是掌握寫heap的能力――這才是最本質的代碼能力。
學會用STL是件很爽的事情。但是須知有所得必有所失。如果過早地接觸STL,會讓你失去很多鍛煉代碼能力的機會。
至于SCL,我的主張是盡量不用。
不可否認,隊里確實有一些人SCL用得很好。但是,我至今仍然沒有見過一個SCL用得很好,同時有擁有很強的代碼能力的人。同樣是有所得必有所失,你平時習慣了去抄程序,必然少了很多自己構思程序的機會,從而影響代碼能力的提高。
當然,我也不是完全反對去使用SCL,偶爾用一下也是可以的,例如在比賽中。但是,需要注意的是,一定要用自己整理的SCL。我見過有人拿著一本別人整理的SCL,雖然內容很齊整,但是我沒見他用對過。因為這本SCL不是他整理的,他自己都不知道每個程序在使用的時候應該注意些什么,于是一用就錯。
算法名言(含義深刻啊)
1.算法的靈魂――數據結構+算法=程序
2.剪枝是搜索的關鍵。
3.可貪則貪。
4.枚舉是最容易實現的,但也是最慢的。
5.難題往往需要另辟蹊徑。
6.算法并不是孤立的,而是可以結合在一起的。
7.不做爛題水平也會下降,但不想難題永遠不會提高。
posted @
2010-10-11 17:37 孟起 閱讀(436) |
評論 (0) |
編輯 收藏
問題是這樣的:問用n條直線最多能將平面分成多少個區域?
這也是一個很簡單的遞歸問題: L[n] = L[n-1] + n; (L[0] = 1)
通項公式如下:L[n] = n * (n + 1) / 2 + 1 ( n>= 0 )
如果不用直線的話,用一個一般的折線,那么n個這樣的折線最多可以拆分平面:
D[n] = L[2*n] - 2 * n;
D[n] = 2 * n ^ 2 - n + 1;
如果用"Z"字型的線,n個折線最可拆分平面:
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=652
Z[n] = Z[n-1] + 9*n - 8;
Z[n] = (9*n^2 - 7*n + 2) / 2;
1 #include<stdio.h>
2 int main()
3 {
4 int n;
5 while(scanf("%d",&n)!=EOF){
6 printf("%d\n",(9*n*n-7*n+2)/2);
7 }
8 return 0;
9 }
posted @
2010-10-11 10:45 孟起 閱讀(395) |
評論 (0) |
編輯 收藏
每個符號三角形都是由它的第一行“+,-”號分布決定的,據此可演算出所有分布的三角形,對其進行統計即可。
同時將一個n行三角形T的+,-號個數分別記為pos_num(n),neg_num(n),其第一行中的+,-號個數記為x(n),y(n),則可得到下式:
pos_num(n)=x(n)+pos_num(n-1)
neg_num(n)=y(n)+neg_num(n-1)
由此,我們可以從n=1開始,利用前面n=k-1的結果,迭代求出n=k的分布情形,然后對n=k的所有分布統計。
#include<iostream>
#include<vector>
#include<cmath>
using namespace std;

struct record
{
int pos,neg;

record(int a,int b)
{
pos=a; neg=b;
}
};
int main()


{
int n,i,j,k,sum;vector<record> v;
for(int m=1;m<=24;m++)

{
n=m;

if((n*(n+1))%4!=0)
{
cout<<n<<" 0"<<endl;
continue;
}
vector<record> v;
record r1(0,1);//n=1的情況
v.push_back(r1);
record r2(1,0);
v.push_back(r2);
for(i=2;i<=n;i++)//計算到n的所有情況

{
int * trip=new int[i];
int sum_i=(int)pow(2.0,i*1.0);
for(j=0;j<sum_i;j++)//第j種分布

{
int temp1=j, temp2=i;
int x=0, y=0; //記錄+,-的個數
while(temp1)

{

if(temp1%2==0)
{
trip[--temp2]=0; y++;
}

else
{
trip[--temp2]=1; x++;
}
temp1/=2;
}
for(k=0;k<temp2;k++)
y++, trip[k]=0;
int idx=0;
for(k=0;k<i-1;k++)

{
if(trip[k]+trip[k+1]==1)
idx*=2;
else idx*=2,idx+=1;
}
x+=v[2*((int)pow(2.0,i-2.0)-1)+idx].pos;
y+=v[2*((int)pow(2.0,i-2.0)-1)+idx].neg;
record r(x,y);
v.push_back(r);
}
}

/**//*if(n==3){
int star=2*((int)pow(2.0,n-1.0)-1);
for(j=0;j<(int)pow(2.0,n*1.0);j++)
printf("---%d %d\n",v[star+j].pos,v[star+j].neg);
}*/
int base=2*((int)pow(2.0,n-1.0)-1);
int num=(int)pow(2.0,n*1.0);
sum=0;

for(i=0;i<num;i++)
{
if(v[base+i].pos==v[base+i].neg)
sum++;
}
cout<<n<<" "<<sum<<endl;
}
return 0;
}
題中,n<=24,時間空間均有限制,我們可以先求出所有結果,然后保存到數組直接取來輸出。這是ACM題中很常見的情況。
1 #include<stdio.h>
2 int res[25]={0,0,0,4,6,0,0,12,40,0,0,171,410,
3 0,0,1896,5160,0,0,32757,59984,0,0,431095,822229};
4 int main()
5 {
6 int n;
7 while(scanf("%d",&n),n)
8 {
9 printf("%d %d\n",n,res[n]);
10 }
11 return 0;
12 }
posted @
2010-10-11 09:13 孟起 閱讀(511) |
評論 (0) |
編輯 收藏