【數據結構】基礎數據結構和算法

搜索

###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問題:

  1. 描述一個最優解的結構(劃分問題)
  2. 遞歸地定義最優解的值(狀態轉移)
  3. "自底向上"計算最優解(初始化+記憶化搜索)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章