写在前面 由于本课程是第一次面向本科专业开设,我身为第一届应考生,承担的考试压力要大许多。一是因为没有历年的真题供参考,另一方面是自己也并没有把太多心思放在这门课程的学习上。好在老师给出了复习提纲和样卷。那么我们就按照复习提纲和样卷来做一个全面的复习。
2021考题预测 1.插入算法
2.Master定理
3.DFS/BFS
4.回溯方法解决子集树/排列树的搜索问题
5.分支界限方法解决旅行商问题(较难暂时放弃)
6.Las解决0/1背包问题
7.动态规划方法解决多段图问题
8.贪心算法求解装载问题
9.归并排序/快速排序/二分排序
10.回溯方法解决最大团问题/子集和问题
2021期末预测题目全解 必须背下来的算法:
DFS 1 2 3 4 5 6 7 8 9 10 11 12 void DFS (Matrix<bool > &G,int v,vector <bool > visited,Func Visit) { int n = G.Rows(); Visit(v); Visited(v)=1 ; for (int w =0 ;w<n;++w){ if (not Visited[w] and G(v,w)){ DFS(G,w,Visited,Visit); } } }
BFS 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 void BFS (Matrix <bool > &G,int v,vector <bool > Visited,Func Visit) { int n = G.Rows(); queue <int > Q; Visit(v); Visited[v]=1 ; Q.push(v); while (not empty(Q)){ v = Q.front(); Q.pop(); for (int w=0 ;w<n;++w){ if (not Visited[w] and G(v,w)==1 ){ Visit(w); Visited[w]=1 ; push(w); } } } }
回溯方法解决子集树搜索问题 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <dummy.h> void B_SubSet (int n) { vector <int > X (n) ; function <void (int )> SubSet = [&](int ){ if (t>=n) Print X; else { if (legal(t)){ X[t]=1 ; SubSet(t+1 ); } if (bound(t)){ X[t]=0 ; SubSet(t+1 ); } } } }
回溯方法解决排列数搜索问题 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include <dummy.h> void B_Perm (int n) { vector <int > X(n); iota(X); function<void (int )>Perm=[&](int t){ if (t>=n-1 ) cout <<X<<endl ; else { for (int i =t;i<n;++i){ swap(X[t],X[i]); if (legal(t) and bound(t)) Perm(t+1 ); swap(X[t],X[i]); } } } }
Las解决0/1背包问题 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <LasVegas.h> bool Knap (vector <double > &V,vector <double > &W,double c,double t,vector <bool > &X) { static mt19937g; int n = min(size(V),size(W)); double fv = 0 ,fw = 0 ; for (int i =0 ;i<n;++i){ X[i]=g()%2 ; fv+=V[i]*X[i]; fw+=W[i]*X[i]; if (fw>c) return false ; } return fv>=t; }
贪心算法求解装载问题 1 2 3 4 5 6 7 8 9 10 11 auto Load (vector <double > &W,double M) { int n =size(W); sort(W); vector <bool >X(n,0 ); double rc = M; for (int i=0 ;i<n and W[i]<=rc;++i){ X[i] = 1 ; rc- = W[i]; return X; } }
回溯方法解决最大团问题 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Clique(t,cn) { if (t>=n and cn >fn){ fn = cn; BX = X; }else if (t<n){ if (Connected(G,X,t)) { X[t]=1 ; Clique(t+1 ,cn+1 ); } if (cn+n-t>fn){ X[t]=0 ; Clique(t+1 ,cn); } } }
回溯方法解决子集和问题 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 SetSum(t,s,r){ if (not Y.empty()) return ; if (s==M) print X; else if (t<n){ if (s+W[t]<=M){ X[t]=1 ; SetSum(t+1 ,s+W[t],r-W[t+1 ]); } if (s+r-W[t]>=M){ X[t] = 0 ; SetSum(t+1 ,s,r-w[t+1 ]); } } }
归并排序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #include <algorithm.h> template <class T >void Merge (T X[],int low,int m,int up) { T W[up-low]; int i = low,j = m,k=0 ; for (;i<m and j<up;k++) { if (X[i]<X[j]) { W[k] = X[i]; i++; }else { W[k] = X[j]; j++; } } for (;i<m;i++){ W[k] = X[i]; k++; } for (;j<up;j++){ W[k] = X[j]; j++; } void MergeSort (T X[],int low,int up) { if (up-low<=1 ) return ; int m =(low+up)/2 ; MergeSort(X,low,m); Mergesort(X,m,up); Merge(X,low,m,up); }
快速排序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include <algorithm.h> template <class T > int Partition (T X[], int low, int up) { int key = up-1 ; int m = low; for (int i =low;i<up;i++){ if (X[i]<X[key]){ swap(X[i],X[m]); m++; } } swap(X[key],X[m]); return m; }void QuickSort (T X[],int low,int up) { if (up-low<=1 ) return ; int m =Partition(X,low,up); QuickSort(X,low,m); QuickSort(X,m+1 ,up); }
二分搜索 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <algorithm.h> template <class T1 ,class T2 >int search (T x[],int n,const T2 &v) { int low = 0 ; int up = n; while (low < up){ int m =(low+up)/2 ; if (X[v]<X[m]){ up = m; } if (X[v]>X[m]) low = m+1 ; if (X[v]==X[m]) return m; } return -1 ; }
插入算法 1 2 3 4 5 6 7 8 9 10 11 12 template <class T ,class T2 >int insert (T X[],int m,const T2 &V) { int p; for (p = m-1 ;p>0 and X[p]>v;--p) if (X[p]==v) return m; for (int i=m-1 ;i>=p;--i){ X[i+1 ]=X[i]; X[p+1 ] = v; return m+1 ; } }
图的遍历 二叉树的遍历 二叉树的C++表示 1 2 3 4 5 6 7 #include <algorithm.h> template <class T >struct BtNode //二叉树结点{ T data; BtNode *left = 0 ,*right = 0 ; };
二叉树的先根次序遍历 1 2 3 4 5 6 7 8 9 template <class T ,class Func >void PreOrder (BtNode<T> *x ,Func Visit) { if (x==0 ) return ; Visit(x) PreOrder(x->left,Visit); PreOrder(x->right,Visit); }
中根遍历与后根遍历只是调换了顺序,故不再赘述。
二叉树的按层次遍历 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 template <class T ,class Func >void LevelOrder (BtNode<T> *x,Func Visit) { queue <BtNode<T> *> Q; if (x!=0 ){ Visit(x); Q.push(x); } while (not empty(Q)){ x = Q.front(); Q.pop(); auto left = x->left; auto right = x->right; if (left!=0 ) { Visit(left) Q.push(left); } if (right!=0 ){ Visit(right) Q.push(right); } } }
一般树的遍历 树的C++表示 1 2 3 4 5 6 #include <algorithm.h> template <class T >struct TreeNode { T data; TreeNode *first = 0 ,next = 0 ; }
树的先根遍历 1 2 3 4 5 6 7 8 9 10 template <class T ,class Func >void PreOrder (TreeNode<T> *x,Func Visit) { if (x==0 ) return ; Visit(x); PreOrder(x->first,Visit); for (x=x->first;x!=0 ;x=x->next){ PreOrder(x->next,Visit); } }
树的中根遍历只是与先根遍历的顺序不同,不再赘述。
树的后根遍历 1 2 3 4 5 6 7 8 9 10 11 template <class T ,class Func >void PostOrder (TreeNode<T> *x,Func Visit) { if (x==0 ) return ; auto t = x->first; PostOrder(t,Visit); for (;t!=0 ;t=t->next){ PostOrder(t>next,Visit); Visit(x); } }
树的按层次遍历 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 template <class T ,class Func >void LevelOrder (TreeNode<T> *x,Func Visit) { queue <TreeNode<T> *> Q; if (x!=0 ){ Visit(x); Q.push(x); } while (not empty(Q)){ x = Q.front(); Q.pop(); for (x=x->first;x!=0 ;x=x->next){ Visit(x); Q.push(x); } } }
连通图的两种遍历方法 宽度优先搜索BFS 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <algorithm.h> template <class Fun >void BFS (Matrix<bool > &G,int v,vector <bool > &Visited,Fun Visit) { int n =G.Rows(); queue <int > Q; Visit(v); Visited[v] = 1 ; Q.push(v); while (not empty(Q)){ v = Q.front(); Q.pop(); for (int w=0 ;w<n;++w){ if (not Visited[w] and G(v,w)==1 ){ Visit(w); Visited[w]=1 ; Q.push(w); } } } }
深度优先搜索DFS 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <algorithm.h> template <class Fun >void DFS (Matrix<bool > &G,int v,vector <bool > &Visited,Fun Visit) { int n =G.Rows(); Visit(v); Visited[v]=1 ; for (int w =0 ;w<n;++w){ if (not Visited[w] and G(v,w)==1 ){ DFS(G,w,Visited,Visit); } } }
分治方法 折半搜索 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include <algorithm.h> template <class T1 ,class T2 >int search (T x[],int n,const T2 &v) { int low = 0 ; int up = n; while (low<up){ int m = (low+up)/2 ; if (v==X[m]) reuturn m; else if (v<X[m]) up=m; else low = m+1 ; } return -1 ; }
归并排序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #include <algorithm.h> template <class T >void Merge (T X[],int low,int m,int up) { T W[up-low]; int i=low ,j=m,k=0 ; for (;i<m and j<up;++k){ if (X[i]<X[j]) { W[k] = X[i]; ++i; }else { W[k]=X[j]; ++j; } for (;i<m;++k){ W[k]=X[i]; ++i; } for (;j<up;++k){ W[k]=X[j]; ++j; } } }void MergeSort (T X[],int low,int up) { if (up-low<=1 ) return ; int m = (low+up)/2 ; MergeSort(X,low,m); MergeSort(X,m,up); Merge(X,low,m,up); }
快速排序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #pragma once #include <algorithm.h> template <class T > int Partition (T X[], int low, int up) { int key = up - 1 , m = low; for (int i = low; i < key; ++i) if (X[i] < X[key]) swap(X[i], X[m]), ++m; swap(X[key], X[m]); return m; } /void QuickSort (T X[],int low,int up) { if (low>=up) return ; int m =Partition(X,up,low); QuickSort(X,low,m); QuickSort(X,m+1 ,up); }
贪心方法 装载问题 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <algorithm.h> auto Load (vector <double > &W,double M) { int n = size(W); sort(W); vector <bool > X (n,0 ) ; double rc = M; for (int i=0 ;i<n and W[i]<=rc;++i){ X[i] = 1 ; rc- = W[i]; } return X; }
背包问题 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 auto Knap (vector <double > &V,vector <double > &W,double M) { int n = min(size(V),size(W)); Sort(V,W); vector <double > X (n,0 ) ; double rc = M; int t; for (t = 0 ;t<n and W[t]<=rc;++t){ X[t] = 1 ; rc - =W[t]; } if (t<n){ X[t]=rc/W[t]; return X; } }
活动安排问题 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include <algorithm.h> auto Action (vector <double > &S,vector <double > &F) { int n = min(size(S),sieze(F)); Sort(S,F); vector <bool > X (n,0 ) ; X[0 ] = 1 ; for (int i =1 ,j=0 ;i<n;++i){ if (S[i]>=F[j]){ X[i] = 1 ; j = i; } } return X; }
最小生成树问题 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 bool Prim (const Matrix<double > &G,int v,vector <int > &prev) { int n = G.Rows(); prev.assign(n,v); vector <bool > S (n,false ) ; S[v] = true ; for (int i =0 ;i<n-1 ;++i){ double min = inf; for (int w=0 ;w<n;++w){ if (not S[w] and G(prev[w],w)<min) { min = G(prev[w],w); v=w; } } if (isinf(min)) return false ; S[v] = true ; for (int w = 0 ;w<n;++w){ if (not S[w] and G(v,w)<G(prev[w],w)) prev[w] = v; } } return true ; }
动态规划方法 矩阵连乘问题的动态规划方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 auto MatrixChain (int r[],int n) { Matrix<int > c (n,n,0 ) ; Matrix<int > kay (n,n) ; for (int i =n-2 ;i>=0 ;--i){ for (int j = i+1 ;j<n;++j){ c(i,j) = (int )inf; for (int k =i;k<j;++k){ int t = c(i,k)+c(k+1 ,j)+r[i]*r[k+1 ]*r[j+1 ]; if (t<c(i,j)){ c(i,j) = t; kay(i,j) = k; } } } } }
多段图问题 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 auto MultiGraph (Matrix<double > &G) { int n = G.Rows(); t = n-1 ; vector <double > C (n,0 ) ; vector <int > Next (n) ; for (int j =t-1 ;j>=0 ;--j){ int r = j+1 ; for (int i =r;i<n;++i){ if (G(i,j)+C[i]<G(j,r)+C[r]) r = i; C[j] = G(j,r)+C[r]; Next[j] = r; } } vector <int > X (m) ; X[0 ] = 0 ; for (int i = 0 ;i<m-1 ;++i){ X[i+1 ] = Next[X[i]]; } return X; }
回溯方法 子集和问题 1 2 3 4 5 6 7 8 9 10 11 12 13 14 SetSum(t,s,r){ if (s==M) print X; else if (t<n){ if (s+W[t]<=M){ X[t] = 1 ; SetSum(t+1 ,s+W[t],r-W[t+1 ]); } if (s+r>=M){ X[t] = 0 ; SetSum(t+1 ,s,r-W[t+1 ]); } } }
旅行商问题 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <algorithm.h> auto TSP (Matrix<double >&G) { int n = G.Rows(); vector<int> X(n),BX;//到当前结点的路经,最优路径 iota(X); double BC = inf; function<void (int ,double )> TSP=[&](int t,double C){ auto LC = C+G(X[t-1 ],X[t])+G(X[t],0 ); if (t>=n-1 and LC<BC){ BC = LC; BX = X; }else if (t<n-1 ){ for (int i = t;i<n;++i){ swap(X[t],X[i]); auto CC = C +G(X[t-1 ],X[t]); if (CC<BC){ TSP(t+1 ,CC); } swap(X[t],X[i]); } } } }
最大团问题 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function Clique (t,cn) { if (t>=n and cn>fn) { fn = cn; BX = X; }else if (t<n){ if (Connected(G,X,t)){ X[t] = 1 ; Clique(t+1 ,cn+1 ); } if (cn+n-t>fn){ X[t]=0 ; Clique(t+1 ,cn); } } }
概率方法 0/1背包问题 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <LasVegas.h> bool Knap (vector <double > &V,vector <double > &W,double c,double t,vector <bool > &X) { static mt19937g; int n =min(size(V),size(W)); double fv = 0 ; double fw = 0 ; for (int i = 0 ;i<n;++i){ X[i] = g()%2 ; fv+=V[i]*X[i]; fw+=W[i]*X[i]; if (fw>c) return false ; } return fv>=t; }