搜索
###DFS 深度搜索:連通性問題
模板中,參數控制解答樹的高度,內循環控制樹的廣度
dfs(int k){
if(k==n){
// 執行 P(m,n)=m!/(m-n) 次
return;
}
for(int i=k; i < m; i++){
dfs(k+1);
}
}
記憶化搜索:路徑壓縮
思想: 對於已經搜索過並足夠小的路徑,記憶搜索結果備下次使用
//並查集優化
1. 並查集優化
int parent(int x){
return (a[x]==x)? x : a[x]=parent(a[x]);
}
2. a+b組不成的最大數:
dfs[x]=(dfs[x-a]||dfs[x-b])?1:0;
if(!dfs[x]){
ans=max(ans,x);
}
###BFS 寬度搜索: 最小換乘問題
int g[n][n],vis[n],arr[n],ap=0;
//初始化
vis[0]=1;
arr[ap++]=0;
//遍歷隊列
for(int i=0;i < ap; i++){
int k=a[i];
//code for deal node k;
for(int j=0;j < n;j++){//拓展隊列
if(g[k][j] && !vis[j]){
arr[ap++]=j;
vis[j]=1;
}
}
}
##數論
//線性時間打素數表
int arr[(int)(n/log(n)*1.5)],ap,vis[n];
for(int i=2; i < n; i++){
if(!vis[i])arr[ap++]=i;
for(int j=0; j < ap && i*a[j] < n;j++ ){
vis[i*a[j]]=1;
if(i%a[j] == 0)break;
// num => abx,其中a,b爲素數,這樣的num會被篩兩次
}
}
唯一分解定理
數字被分解的結果是唯一的。
歐拉函數:euler(n) = n(1-1/q[i])*
其中q爲n唯一分解表中的素數,歐拉函數值是1-n中與n互素數的個數;
互素測試(輾轉相除):
int gcd(int x,int y){
//Fibonacci級減少
return (y==0) ? x : gcd(y, x%y);
}
##圖論
1. 最小生成樹:連通一個圖的所有節點,所消耗總代價最少(電話連線問題)
2. Dijkstra最短路徑:單源點到其他各點的最短路徑 - O(n^2)
3. Floyd最短路徑:圖中任意兩點最短路徑 - O(n^3)
##貪心模型(啓發式算法)
證明無後效性:對具有最優子結構的一維問題,每次決策都直接影響結果,並不可回退(前面狀態->當前狀態->後面狀態);
1. 最優裝載問題(部分揹包問題):儘量裝載單位體積價值大的物品
2. Haffman可變長編碼:出現頻率高的字符,編碼長度儘量短
3. Prim算法:每次找出距離當前【集】最近的結點
4. Dijkstra算法:每次找出距離【源點】最近的結點
5. krusal算法:每次找出兩個端點 不在同一集中 且 長度最短的邊
##分治模型
可分解:問題可分解成和原問題 性質相同 且 相互獨立 的若干子問題;
可合併:若干子問題的解可合併得出原問題的解;
1. 二分法: 原問題分解成兩段【快速冪】
2. 第K小的數: [1,n]求k => [1,n/2)或(n/2,n]求k - 快排
3. 逆序對數: 一個序列,相鄰元素可以交換,求交換多少次序列有序 - 歸併
##動態規劃模型
DP-貪心: DP中局部最優解不一定是全局最優解;
DP-分治: DP劃分的子問題不具備獨立性;
1. 階段求解(遞推式)
1. 上N階臺階方案數: 一次上1階或2階,求上到n臺階的總方案數;
對於 i 臺階,能達到的臺階爲 i-1和i-2; 故 a[i]=a[i-1]+a[i-2];
2. RPG難題: 對於N個空位,使用R,P,G填充,要求相鄰和首尾不同,求總方案數;
當i>=4時: 若 p[i-1] == p[0] 則 a[i]=2*a[i-1];
若 p[i-1] != p[0] 則 a[i]=a[i-2];
3. 危險的組合: 對於N個格子擺放0和1要求至少3個0連續擺放,求總方案數;
當i>=4時: 若前i-1個已經符合要求 則 a[i]=2*a[i-1]
若前i-1個不符合時i必須放0 則 a[i]=2^(i-3) - a[i-3];
4. 全錯位排列:
對於_(___) 若 _與(____)中恰好交換:a[i]=a[i-2]*(i-1)
若 _與(____)中不是交換:a[i]=a[i-1]*(i-1),此時把_在(___)中視爲正確擺放
5. 排隊問題: 隊伍中m個5和n個10,初始無5元找開,求合法排隊方案數(同樣錢的人交換視爲同一種)
對於隊伍m+n: 若最後一人持5元 則方案數 a[m][n] = a[m-1][n]
若最後一人持10元 則方案數 a[m][n] = a[m][n-1]
初始化:a[i][0]=1; a[i][i+x]=0;
2. 動態決策
1. 【線性】
最長公共子序列: 對於每個i,討論j=[1,n],dp[j]表示在逆序遍歷j位置時最長公共序列長
從後往前遍歷s和t, 若s[i]==t[j] 則 dp[j]=dp[j+1]+1;
否則 dp[j]=max(dp[j],dp[j+1]);
最長上升子序列: 對於每個i,dp[i]表示在i位置的最長上升序列長度
順序遍歷a[N]:對於a[i]往前找到一個j使得a[i]>a[j] 則 dp[i]=dp[j]+1;
3. 【揹包】
0-1揹包問題:對於每個容量c放a[i]物品,放或不放形成兩個決策結果
對於dp[c] 若c>=v[i] 則 dp[c]=max(dp[c-a]+v,dp[c]);
注意: 此處要求逆序遍歷容量c;
完全揹包問題(物品無限)
同0-1揹包問題狀態轉移 dp[c]=max(dp[c-a]+v,dp[c]);
注意: 對於容量要求順序遍歷,這樣才能保證多次放入i物品
DAG定點最長/短路徑: 硬幣找零問題,劃分當前金額數爲子結構
對於當前金額i dp[i]=max(dp[i-v[j]]+1,dp[i])
注意: 對於當前金額要順序遍歷,保證同一硬幣可以多次加入;
6. 【劃分】
整數劃分:把n劃分爲k份的方案數
對於dp[n][k] 若 n>=k dp[n][k]=dp[n-k][k]+dp[n][k-1];
若 n < k dp[n][k]=dp[n][k-1];
初始化: dp[n][1]=1;
7. 【區域】
石子合併:相鄰合併若干堆石子,代價爲w[i]+w[i+1],求最小總代價
對於dp[i][j]表示合併石子i->j的最優解;定義k=[i,j]則 dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+w[j]-w[i-1]);最後值爲dp[1][n];
初始化: dp[i][j]=INF,dp[i][i]=0; w[i]表示1-i石子代價總和;
Floyd:
找到一箇中間點k,用k插入到所有的[i,j]區間並更新[i,j]區間長度
9. 【樹形】數字三角形
對於以i爲根的樹,dp[i]=dp[i]+max(dp[2*i],dp[2*i+1]);
DP問題:
- 描述一個最優解的結構(劃分問題)
- 遞歸地定義最優解的值(狀態轉移)
- "自底向上"計算最優解(初始化+記憶化搜索)