市赛前第四次培训讲义
An-Li Alt Ting October 20, 2014
Contents
1 Preface 2
2 Algorithm Design Paradigm 2
2.1 Enumeration 枚举 . . . 2
2.2 Approximation Algorithms 近似算法 . . . 2
2.3 Divide and Conquer 分治法 . . . 3
2.4 Dynamic Programming 动态规划 . . . 4
2.5 Greedy Algorithms 贪婪算法 . . . 6
2.6 Integer Programming 整数规划 . . . 7
3 数论 7 3.1 素数 . . . 7
3.2 斐波那契数列 . . . 7
3.3 卡塔兰数 . . . 8
3.4 整數分拆 . . . 8
3.5 貝祖等式 . . . 8
3.6 Stern–Brocot tree . . . 8
4 排列组合 8 5 线性代数 9 5.1 Matrix 矩陣 . . . 9
6 抽象代数 10 6.1 Multiplication Inverse Element about Modular . . . 10
7 经典问题 11 7.1 Listing Prime Numbers 列出質數 . . . 11
7.2 Greatest Common Divisor (GCD) 最大公因數 . . . 12
7.3 01-matrix - 最大全 1 子矩形 . . . 12
7.4 01-matrix - 最大全 1 子正矩形 . . . 13
7.5 Knapsack Problem 背包問題. . . 13
7.6 Longest Common Subsequence (LCS) 最長共同子序列 . . . 14
7.7 Longest Increasing Subsequence (LIS) 最長遞增子序列 . . . 14
7.8 Matrix Chain Multiplication 矩陣鏈乘積 . . . 15
8 References 15
1 Preface
原本的四训有被段考吞噬的迹象,所以这次四训会多有原本五训的一半的课,五训则 变为剩下的一半与六训的课,六训就没了。
大家加油啦,包办市赛一二等奖哦!
2 Algorithm Design Paradigm
Algorithm Design Paradigm 算法设计範式,是一种算法的类别。
不过在多数情况下,算法设计範式的定义是比较模糊的,在实用的角度上过度强 其定义是没有意义的。举例来说,Dijkstra’s Algorithm 在没有详细定义问题时,也是 很难(不是不能)严谨定义的算法,那么 Dijkstra’s Algorithm 就是一个算法设计範式 而非算法,然而这在一般情况下是很不实用的想法。
笔者认为这是比算法更接近哲学的话题,必须强调「存在先于本质」:这些想法都 是人为了更有系统的解决问题而构造出来的。在某种意义上,就算一个理论是不相容 的,只要它足够解决问题,都是被允许的。主观来说,能用範式 P 理解问题Q,Q 就 是 P 的子集能解决的问题。算法 A 是否属于範式P,取决于描述A 的格式。
2.1 Enumeration 枚举
枚举是一种算法设计範式。
它是很基本的,几乎所有搜寻算法都可以在它的框架下。一个搜寻算法不是没有 枚举,而是枚举的母集不同。
2.1.1 Problems
Listing Prime Numbers 列出質數
2.1.2 Problems on OJs HOJ 024 阿里不達轟
2.2 Approximation Algorithms 近似算法
对于一个最优化问题,放弃取得最优解的保证,则经常可以使用更少的资源解决它。
近似算法一般只会在问题足够难时才使用。
台湾地区长称一些不正统的解法为「喇赛」,这种解法经常是近似算法。
2.2.1 常见的近似算法
Simulated Annealing 模擬退火 爬山算法
2.2.2 Problems on OJs HOJ 109 建橋問題
TIOJ 1797 Fus Ro Dah
2.3 Divide and Conquer 分治法
分治法是一个based on recursion 的算法设计範式。
D&C 可能不会把问题变小,而只把问题清楚的切开,有系统地规约成规模较小的
多个问题。
在 D&C中,把问题分解成不相交的子问题是重要的素养与技巧。
D&C 经常被用于解决组合问题。
2.3.1 特殊的 D&C D&C on Tree樹分治
2.3.2 Algorithms those depend on D&C DFS and BFS
Binary Search 二分搜尋
2.3.3 Data Structures those depend on D&C Tree樹
2.3.4 Problems Tower of Hanoi
2.3.5 Problems on OJs HOJ 109 建橋問題
2.4 Dynamic Programming 动态规划
Dynamic Programming (DP) 动态规划,是一种 based on D&C 的算法範式。笔者主 观相信,动态的意思是在组合子问题的解时,经常是带有判断的。
与 D&C 相比,DP 更倾向于使用子问题的解结合出母问题的解,而不是单纯解决
子问题就好。尽管在某种意义上,不浪费资源的算法都需要使用子问题的解结合出母 问题的解。
与 D&C 相比,DP 更倾向于设计出容易有重复子问题的状态转移方式,这一般是
为了节省时空资源。
与 D&C 相比,DP 只解决最优化问题;并且依赖「最优子结构」,即母的问题解
由子问题的解组合出时,子问题能总是模型相同的最优化问题。
在某种意义上,DP 不被限于解决规划问题,它也经常被使用与解决组合问题;又 或者说,许多组合问题都可以规约为可以被 DP 所解决的规划问题。把计数问题规约 成规划的相法就是尽量多。
2.4.1 Optimization
1D/1D 問題的單調性
concave totally monotone 凹單調性 Ai,k ≤Aj,k →Ai,l ≤Aj,l
convex totally monotone 凸單調性 Ai,k ≥Aj,k →Ai,l ≥Aj,l
轉移的方法相似時,可以把轉移組合在一起。並且,可以允許組合到不會導致答 案更好的解,以建立更通用的組合轉移。只要另外補回獨立轉移,就不會導致答案更 差。
尋找狀態的不變量,可以有小縮小狀態空間。
2.4.2 特殊的 DP DP on a Tree 樹DP
2.4.3 Problems
Calculating Fibonacci Number 01-matrix - 最大全 1子矩形 01-matrix - 最大全 1子正矩形 Knapsack Problem 背包問題
Longest Common Subsequence (LCS) 最長共同子序列 Longest Increasing Subsequence (LIS) 最長遞增子序列 Matrix Chain Multiplication 矩陣鏈乘積
2.4.4 Problems on OJs HOJ 033 最大子矩陣
HOJ 037 高空煙火時間限制
HOJ 044 A game HOJ 045 Miners HOJ 048 買醬油 I
HOJ 049 今天我生日ㄛ
HOJ 062 Words
HOJ 053 買醬油 II HOJ 135 縫紉課 HOJ 192 撿肥皂 HOJ 216 快遞服務 HOJ 296 烏龜棋
IOICJ 003 ATP 與路跑
IOICJ 015 小可魚 VS.大可魚 IOICJ 047 球的進化
IOICJ 056 最佳二元搜尋樹 DP Optimization.
IOICJ 059 A+ 遊戲 DP Optimization.
IOICJ 062 柳橙題 TIOJ 1029 A 遊戲
2.5 Greedy Algorithms 贪婪算法
贪婪是一个算法设计範式。
贪婪思想可以在状态转移的意义上被理解。它利用启发式函数选择下一个状态。
为了节省资源,该启发式函数可能只分析状态的表征,就作出决策。
贪婪思想用于一个最优化算法,总是由当前状态转移到邻近状态中「不会让优化 目标变差」的其中一个状态。由数学归纳法可知,这种转移策略会结束在最优解上。
2.5.1 Algorithms those depend on Greedy 爬山算法
Dijkstra’s Algorithm Prim’s Algorithm
Kruskal’s Algorithm
Hungarian Algorithm 匈牙利算法 Ford-Fulkerson Algorithm
2.5.2 Problems on OJs HOJ 047 Logs
HOJ 159 派對 HOJ 281 Balls HOJ 318 晚餐時間
HOJ 336 廷廷的大秘寶
2.6 Integer Programming 整数规划
整数规划是变数只能为整数的线性规划,属于 NP-Hard。
许多问题都可以被规约为整数规划,其中包含:
Travelling Salesman
Vertex Cover and other Covering problems Set packing and other Packing problems Boolean satisfiability
3 数论
3.1 素数
¬∃ 2≤x2 ≤n s.t. x|n ⇐⇒ n ∈P
An upper bound for π(n) is given by π(n) < 1.25506nlnn for n > 1, and a lower bound by lnnn < π(n) forn ≥17(Rosser and Schoenfeld 1962).
3.2 斐波那契数列
Fn =
{ n 0≤n <2 Fn−2+Fn−1 otherwise
3.3 卡塔兰数
Cn= n+11 ( 2n
n )
3.4 整數分拆 3.5 貝祖等式
ax+by=m 有解当且仅当 gcd(a, b)|m。
3.6 Stern–Brocot tree
4 排列组合
加法原理:完成一事件有 n 类方法,第i 类方法有 Ai 个方法,完成该事件共有 ∑ Ai 个方法。
乘法原理:完成一事件有 n 个步诹,第 i 个步诹有 Ai 个方法,完成该事件共有
∏Ai 个方法。
排容原理:|∪
Ai|=∑
|Ai| −∑
|Ai ∩Aj|+∑
|Ai∩Aj ∩Ak|. . .
阶乘:n! =
{ 1 n= 0 n(n−1)! otherwise
}
排列:Pkn= (n−n!k)!
组合:Ckn= Pk!kn
重复组合:Hkn=Ckn+k−1
5 线性代数
5.1 Matrix 矩陣
用矩陣來表達二元函数的話,累积的動作可以使用 binary DP,將時間複雜度從 O(n) 降到 O(log n)。
0 既是乘法零元,又是加法單位元,內積也只是乘完再加,矩陣乘法也只是作很多 次內積。所以只要允許填零,矩陣要多大都沒問題。
矩陣可以看作向量的向量。如同向量表達一元函數,矩陣表達二元函數。
struct sm{
int a[mn ][ mn ];
sm( int n=0 , int *b =0){
memset (a ,0 , sizeof a);
if(n ==1) F(mn )a[i][i ]=1;
if(b) memcpy (a ,b, sizeof a);
}
sm operator *( const sm m) const { sm x (0);
F(mn)FO(j,mn)FO(k,mn)
x.a[i][j ]+= a[i][k]*m.a[k][j];
return x;
} };
5.1.1 Tricks
Using __gnu_cxx::power().
5.1.2 Problems on OJs HOJ 007 微波爐
HOJ 108 PTM序列
HOJ 209 買醬油 III之污漬問題 Step5 0123 神秘的西瓜
Step5 0137 字串
6 抽象代数
6.1 Multiplication Inverse Element about Modular
Multiplication Inverse Element about Modular 關於模的乘法逆元素。
給定 k ≡ac ≡bc (mod m)。當 gcd(c, m) = 1,同餘有除法原理:ac≡ bc (mod m) → a ≡ b (mod m)。但是當 c 不整除 k,就不能用 k ÷ c ≡ b (mod m) 來求取 x ≡ b (mod m)。
定義 n−1 為 n 對於模 m 在的乘法逆元素,而非 n 的倒數。我們可以嘗試找到 n−1, 自然 k×n×n−1 ≡k (mod m),類似於我們在普通的算術系統中的除法。
n−1 存在,若且唯若 gcd(n, m) = 1,省略證明。
假設 n−1 存在,即要求 ∀ k : k ×n ×n−1 ≡ k (mod m)。可以使用者歐基里德演 算法求得一個 n−1 的解。或者如果 m 為質數,又能用費馬小定理求得。這兩種算法 的時間複雜度都是 O(log n)。
6.1.1 費馬小定理
定理描述:ap ≡a (mod p),p 為一質數。
由於nm×n−1×n−1 ≡n×n−1×n−1 (mod m)→nm−2 ≡n−1 (mod m),所以nm−2 就
是 n−1 的一個解。
6.1.2 歐基里德演算法
由於 n 與 m 互質。求 n×n−1 ≡ 1 (mod m) 中的 n−1 等價於求不定方程 nx+my = 1 =gcd(n, m) 中的x。又由於 1n+ 0m=n 而且0n+ 1m =m,所以可以利用歐基里 德演算法中 gcd(n, m) 的計算過程,將 1n+ 0m 與 0n+ 1m 分別比照 n 與 m 的之間 的計算。得到gcd(n, m) = 1 以後,與1 相等的式子中,n 前面的係數就是n−1。
6.1.3 C++98 Code
int euclidean ( int a , int b){
int m=b,x=1 ,y =0;
while ((x -=a/b*y,a%=b )&&(y -=b/a*x,b %=a ));
return (x+y+m)% m;
}
7 经典问题
7.1 Listing Prime Numbers 列出質數
7.1.1 Sieve of Eratosthenes 埃氏篩法
# include < bits / stdc ++.h>
using namespace std ; const int n=1 e7;
bool a[n];
int main (){
int cp =0;
for ( int i =2;i<n;i ++) if (!a[i ]){ cp ++;
if(i <=(n -1)/ i) for ( int j=i*i;j<n;j+=i) a[j ]=1;
}
printf ("% i\n",cp );
}
7.1.2 線性篩法
不重複枚舉合數並刪去。
# include < bits / stdc ++.h>
using namespace std ; const int n=1 e7;
bool a[n];
int nxt [n], prv [n];
int main (){
int cp =0 , cc =0;
for ( int i =0;i<n;i ++)
nxt [i]=i+1 , prv [i]=i -1;
for ( int i =2;i<n;i= nxt [i ]){ cp ++;
for ( int j=(n -1)/ i;i <=j;j= prv [j ]){
int k=i*j, pr= prv [k], nx= nxt [k];
if (!a[k ]){ cc ++;
a[k ]=1;
nxt [pr ]=nx , prv [nx ]= pr ; }
} }
printf ("% i\n",cp );
assert (n <2||2+ cp +cc ==n);
}
7.1.3 Problems on OJs
ACOJ 0005 Counting Prime Numbers TIOJ 1036 How Many Primes ?
7.2 Greatest Common Divisor (GCD) 最大公因數
C code:
gcd (a,b){
while (b &&( a%=b )&&( b%=a ));
return a+b;
}
7.2.1 Problems on OJs HOJ 023 [互動題]G.C.D
7.3 01-matrix - 最大全 1 子矩形
給定 A < m, n > 。
Bi,j =
0 Ai,j = 0
1 Ai,j ̸= 0∧i+ 1 =m
Bi+1,j+ 1 otherwise
使用單調棧求取 max
0≤i<m,0≤j,k<n ((min
j≤l<kAi,l)×(k−j))。
7.3.1 Problems on OJs HOJ 129 最大矩形
7.4 01-matrix - 最大全 1 子正矩形
7.4.1 Problems on OJs HOJ 116 哇!好正!
7.5 Knapsack Problem 背包問題
背包問題是一種簡單的規劃模型。
背包問題的定義角度是重要的。
可以使用 DP,在偽多項式時間內解決。
抽象化:
有 n 個物品,第 i 個物品描述重量 Wi 價值 Vi,可選擇一個。求重量和 m 下的最大 價值。
有 n 個方案,第i 個方案描述成本Ci 獲利 Pi,可執行一次。求成本和 m 下的最大獲 利。
零一背包問題。
有 n 個方案,第i 個方案描述成本Ci 獲利 Pi,可執行一次。求成本和 m 下的最大獲 利。
有限背包問題,又稱多重背包問題。
有 n 個方案,第 i 個方案描述成本 Ci 獲利 Pi,可執行 Ti 次。求成本和 m 下的最大 獲利。
位元分解。
無限背包問題,又稱完全背包問題。
有 n 個方案,第i 個方案描述成本Ci 獲利 Pi,可執行無限次。求成本和 m 下的最大 獲利。
7.5.1 Problems on OJs
ACOJ 0008 今天我生日唷!
無限背包問題。
HOJ 041 烤箱 無限背包問題。
HOJ 042 奶牛博覽會 零一背包問題。
HOJ 043 太空電梯 零一背包問題。
HOJ 112 生物實驗 有限背包問題。
IOICJ 003 ATP 與路跑 有限背包問題。
7.6 Longest Common Subsequence (LCS) 最長共同子序列
計算一個序列的LCS 長度可以使用DP 求取,或者轉化為 LIS問題求解。
F(n)FO(j,m)
a[i +1][ j +1]= b[i ]== c[j]?a[i][j ]+1: max (a[i +1][ j],a[i][j +1]);
A 的 LIS 即是A 與 sorted A 的 LCS。
A 與 B 的 LCS 即是所有匹配元素的索引值數對的 LIS。
7.6.1 Problems on OJs HOJ 031 LCS
7.7 Longest Increasing Subsequence (LIS) 最長遞增子序列
Longest Increasing Subsequence (LIS) 最長遞增子序列。
7.7.1 DP Method Si =
{ 1 otherwise
0≤j<i,amaxj<ai
Sj + 1 ∃j : 0≤j < i, aj < ai }
計算一個序列的 LIS 長度,可以使用 DP 或者 Greedy,分別有複雜度 O(n2) 與 O(n log n) 。
LIS 的本質相當於「給定一數對集合 S,求取一數對排列 P ={(Xi, Yi)|0 ≤i < n} 滿 足 ∀ i < j:Xi < Xj ∧ Yi < Yj 」。
7.7.2 Robinson-Schensted-Knuth Algorithm int llis ( int *f , int *l){
int a[l-f] ,* la=a;
for (;f!=l;f ++){
int *p= lower_bound (a,la ,*f);
*(p!= la ?p:la )++=* f;
}
return la -a;
}
7.7.3 Problems on OJs UVaOJ 111 History Grading HOJ 030 LIS
HOJ 032 蜜蜂的約會
HOJ 303 買醬油 IV 之商店問題
成本最小化。
TIOJ 1175 Longest Increasing Subsequence
7.8 Matrix Chain Multiplication 矩陣鏈乘積
7.8.1 Problems on OJs HOJ 046 正直 DE
8 References
http://en.wikipedia.org/wiki/Approximation_algorithm
http://en.wikipedia.org/wiki/Divide_and_conquer_algorithms http://en.wikipedia.org/wiki/Dynamic_programming
http://en.wikipedia.org/wiki/Greedy_algorithm http://en.wikipedia.org/wiki/Integer_programming http://en.wikipedia.org/wiki/Prime-counting_function http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes http://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity http://en.wikipedia.org/wiki/Catalan_number
http://en.wikipedia.org/wiki/Stern%E2%80%93Brocot_tree http://en.wikipedia.org/wiki/Longest_increasing_subsequence http://en.wikipedia.org/wiki/Matrix_chain_multiplication http://mathworld.wolfram.com/PrimeCountingFunction.html http://baike.baidu.com/view/1850916.htm
http://www.baike.com/wiki/%E4%B9%98%E6%B3%95%E9%80%86%E5%85%83 http://zh.wikipedia.org/wiki/%E9%80%86%E5%85%83%E7%B4%A0