第七章至第十四章

第七章复习

引言:

  • 分治算法的不足之处:

    Fibonacci数列:f1=1,f2=1,求f(n);

    递归的方法:

    long fib(n){
        if(n==1 || n==2){
            return 1;
        }
        else{
            return fib(n-1)+fib(n-2);
        }
    }
    

    时间复杂度分析:

    f ( n ) = 1 5 ∗ ( 1 + 5 2 ) n − 1 5 ∗ ( 1 − 5 2 ) n f(n) = \frac{1}{\sqrt 5}*(\frac{1+\sqrt 5}{2})^n - \frac{1}{\sqrt 5}*(\frac{1-\sqrt 5}{2})^n f(n)=5 1(21+5 )n5 1(215 )n

    自底向上的计算方法:

    long fib(n){
        if(n==1||n=2){
            return 1;
        }
        for(i=3;i<=n;i++){
            result =f1+f2;
            f1=f2;
            f2=result;
        }
        return result;
    }
    

    时间复杂度: O ( n ) O(n) O(n);

  • 思考:给出复杂度 O ( log ⁡ n ) O(\log n) O(logn) f ( n ) f(n) f(n)求值算法;

  • 二项式例子不再给出(P130);

  • 启发:

    • 递归的思路分析问题;
    • 自底向上的方法实现;

下面的每一个例子都很重要

最长公共子序列问题

  • 问题:

    • A = <A1A2···An>;
    • B = <B1B2···Bn>;
    • 蛮力的搜索方法: Θ ( m ∗ 2 n ) Θ(m*2^n) Θ(m2n)或者 Θ ( n ∗ 2 n ) Θ(n*2^n) Θ(n2n);
  • 算法思路:

    • 先求最长公共子序列的长度;

    • 定义L[i,j]表示<A1A2…Ai>和<B1B2…Bj>的最长公共子序列的长度,则

      L[i,0]=L[0,j]=0;

      问题的目标是求L[n,m];

      对于任意的L[i,j],当i>0且j>0,我们有

      ​ 如果 A i = B j Ai=Bj Ai=Bj, L [ i , j ] = L [ i − 1 , j − 1 ] + 1 L[i,j]=L[i-1,j-1]+1 L[i,j]=L[i1,j1]+1;

      ​ 如果 A i ≠ B j Ai \neq Bj Ai=Bj, L [ i , j ] = m a x L [ i , j − 1 ] , L [ i − 1 , j ] L[i,j]=max{L[i,j-1],L[i-1,j]} L[i,j]=maxL[i,j1],L[i1,j];

    • 总结:

      L [ i , j ] = { 0 ( i = 0 ∣ ∣ j = 0 ) L [ i − 1 , j − 1 ] + 1 ( i > 0 , j > 0 , a i = b j ) m a x L [ i , j − 1 ] , L [ i − 1 , j ] ( i > 0 , j > 0 , a i ≠ b j ) L[i,j] = \begin{cases} 0(i=0||j=0)\\L[i-1,j-1]+1(i>0,j>0,ai=bj)\\max{L[i,j-1],L[i-1,j]}(i>0,j>0 ,ai \neq bj) \end{cases} L[i,j]=0(i=0j=0)L[i1,j1]+1(i>0,j>0,ai=bj)maxL[i,j1],L[i1,j](i>0,j>0,ai=bj)

    • 初始化:

    for(i=0;i<n;i++){
        L[i,0]=0
    }
    for(j=0;j<m;j++){
        L[0,j]=0;
    }
    
    • 自底向上计算:
    for(i=1;i<n;i++){
        for(j=1;j<m;j++){
            if(a[i]==b[j]){
                L[i,j] = L[i-1,j-1]+1;
            }
            else {
                L[i,j]=max{L[i,j-1],L[i-1,j]};
            }
        }
    }
    return L[n,m];
    
  • 算法优化:

    • 空间复杂度(能否只使用一维数组):
    LCS(){
        Q[1,2,3...,n] = 0;
        for(j=1;j<=m;j++){
            if[Bj=A1] T=1;
            else T=Q[1];
            for(i=2;i<=n;i++){
                if(Bj=Ai){
                    S=Q[i-1]+1;
                    Q[i-1]=T;
                    T=S;
                }
                else{
                    Q[i-1]=T;
                    T=max{T,Q[i]};
                }
            }
            Q[n]=T;
        }
        return Q[n];
    }
    
    • 如何从长度得到最长公共子序列(思路)
      • 我们已经可以得知最长子序列的长度;
      • 然后由最长子序列的长度逐渐回溯找到最长公共子序列;

矩阵链相乘

  • 问题:求出计算n个矩阵相乘M1M2…Mn所需要的最少的乘法次数;
    • 引例:如果M1M2分别是 a ∗ b a*b ab b ∗ c b*c bc的,那么计算M1M2分需要的数量乘法为 a ∗ b ∗ c a*b*c abc次;
    • 计算M1M2M3(其维度分别是10 * 2,2 * 10,10 * 2);
      • 第一种计算顺序:M1M2M3=[M1M2]M3;需要的乘法的次数是200+200=400;
      • 第二种计算书讯:M1M2M3=M1[M2M2];需要的乘法的次数是40+40=80;
    • 蛮力法:列举每一种可能的结合顺序,比较其所需的乘法次数;
      • 假设n个矩阵相乘可能的结合顺序有f(n)种,则(M1M2M3···Mk) * (Mk+1Mk+2···Mn);
      • f ( k ) ∗ f ( n − k ) f(k)*f(n-k) f(k)f(nk),其中k = 1,2,…,n-1;
      • f ( n ) = ∑ k = 1 n − 1 f ( k ) f ( n − k ) f(n) = \sum_{k=1}^{n-1}f(k)f(n-k) f(n)=k=1n1f(k)f(nk) , f ( 2 ) = 1 , f ( 3 ) = 2 f(2)=1,f(3)=2 f(2)=1,f(3)=2;
  • 算法思路:
    • 动态规划:
      • 数组r[1,2,…,n+1]中存储每个矩阵的行数和列数;
      • 设置r[i]存储矩阵Mi的行数,则i>1时,r[i]必然也是矩阵Mi-1的列数;
      • 设置r[n+1]存储矩阵Mn的列数;
    • 最优解的结构:
      • 记Mi,j=MiMi+1…Mj;
      • C[i,j]表示计算所需要的数量乘法的最少次数:(MiMi+1…Mk-1)(MkMk+1…Mj); (其中k=i+1,…,j);
      • C [ i , j ] = m i n ( C [ i , k − 1 ] + C [ k , j ] + r [ i ] ∗ r [ k ] ∗ r [ j + 1 ] ) , ( i < k ≤ j ) C[i,j] = min(C[i,k-1]+C[k,j]+r[i]*r[k]*r[j+1]),(i<k\leq j) C[i,j]=min(C[i,k1]+C[k,j]+r[i]r[k]r[j+1]),(i<kj)
      • 这里需要求出的是C[1,n],且C[i,i]=0;
    • 动态规划:自底向上的算法求C[1,n];
      • 计算C[i,j]需要得知同一行的所有值,同一列的所有值;
      • 按照对角线方向计算值,直到找到C[1,n];
  • 算法呈现:
void MatrixChain(){
    for(i=1;i<=n;i++){
        C[i,i]=0;
    }
    for(d=1;d<=n-1;d++){
        for(i=1;i<=n-d;i++){
            j=i+d;
            C[i,j]=C[i+1,j]+r[i]*r[i+1]*r[j+1];
            for(k=i+2;k<=j;i++){
                C[i,j]=min{C[i,j],C[i,k-1]+C[k,j]+r[i]*r[k]*r[j+1]};
            }
        }
    }
    return C[1,n];
}
  • 算法分析:时间复杂度 O ( n 3 ) O(n^3) O(n3);

所有点的最短路径

  • 问题:G=(V,E)有向带权图

    • l[i,j]为i到j的边的权值,如果没有边则为 + ∞ +∞ +
    • V包含了n个顶点,记为{1,2,···,n};
    • 求出任意两个顶点之间的最短路径长度;
    • 最终计算结果的存储:n*n的二维数组
      • 比如D[1,2,…,n] [1,2,…,n],其中D[i] [j]表示i到j的最短路径长度;
      • D0[i] [j]=l[i] [j],Dn[1,2,…,n] [1,2,…,n];
  • Floyd算法:

    • 考虑 d ( i , j ) k , 1 ≤ k ≤ n d(i,j)^{k}, 1\leq k \leq n d(i,j)k1kn d ( i , j ) k d(i,j)^k d(i,j)k d ( i , j ) k − 1 d(i,j)^{k-1} d(i,j)k1的区别在于是否经过顶点k;
    • 从i到j有两种可能
      • 不经过k;
      • 经过k;
    • d ( i , j ) k = m i n ( d ( i , j ) k − 1 , d ( i , k ) k − 1 + d ( k , j ) k − 1 ) d(i,j)^k=min(d(i,j)^{k-1},d(i,k)^{k-1}+d(k,j)^{k-1}) d(i,j)k=min(d(i,j)k1,d(i,k)k1+d(k,j)k1),若 1 ≤ k ≤ n 1\leq k\leq n 1kn; ( D ( k − 1 ) → D ( k ) D(k-1) \rightarrow D(k) D(k1)D(k));
    • 用权值l[i,j]初始化数组D0
    for(k=1;k<=n;j++){
        for(i=1;i<=n;i++){
            for(j=1;j<=n;j++){
                D(k)[i][j]=min{D(k-1)[i][j],D(k-1)[i][k]+D(k-1)[k][j]};
            }
        }
    }
    
    • 算法优化(能否减少空间开销只收用一个数组):
    for(k=1;k<=n;j++){
        for(i=1;i<=n;i++){
            for(j=1;j<=n;j++){
                D[i][j]=min{D[i][j],D[i][k]+D[k][j]};
            }
        }
    }
    

背包问题

  • 问题:物品集U={u1,u2,…,un},背包容量为C,si和vi分别为ui的体积和价值,求出U的子集S,使得 ∑ u i ∈ S s i ≤ C \sum_{ui \in S}si \leq C uiSsiC;常用于密码算法设计中;

    • 定义V[i,j]表示从前i个物品{u1,u2,…,ui},选择若干物品装入j个体积,能够获得最大价值
    • 那么:
      • 若i=0或j=0,V[i,j]=0;
      • 若j<si,V[i,j]=V[i-1,j];
      • i > 0 i>0 i>0 j ≥ j\geq jsi,有两种可能:(1)不装ui(2)装ui;
      • V[i,j] = max{V[i-1,j],V[i-1,j-si]+vi};
    • 总结: V [ i , j ] = { 0 ( i = 0 O r j = 0 ) V [ i − 1 , j ] ( j < s i ) m a x ( V [ i − 1 , j ] , V [ i − 1 , j − s i ] + v i ) ( i > 0 A n d j ≥ s i ) V[i,j] = \begin{cases} 0(i=0 Or j=0)\\V[i-1,j](j<si)\\max(V[i-1,j],V[i-1,j-si]+vi)(i>0Andj\geq si)\end{cases} V[i,j]=0(i=0Orj=0)V[i1,j](j<si)max(V[i1,j],V[i1,jsi]+vi)(i>0Andjsi)
  • 算法呈现:

for(i=0;i<=n;i++){
    V[i,0]=0;
}
for(j=0;j<=C;j++){
    V[0,j]=0;
}
for(i=1;i<=n;i++){
    for(j=1;j<=C;j++){
        V[i,j]<--V[i-1,j];
        if(si<=j){
            V[i,j]=max{V[i,j],V[i-1,j-si]+vi};
        }
    }
}
return V[n,C];
  • 算法分析:时间复杂度 O ( n ∗ C ) O(n*C) O(nC)

  • 算法优化

    • 只使用一维数组:
    void knapsack()
    {
        W[0,1,2,...,C]=0;
        Q[0,1,2,...,C]=0;
        for(i=1;i<=n;i++){
            for(j=1;j<=C;j++){
                Q[j]=W[j];
                if(si<=j){
                    Q[k]=max{Q[j],W[j-si]+vi};
                }
                W[1,2,...,C]=Q[1,2,...,C]
            }
        }
        return W[C];
    }
    
    • 还能进一步减小开销:
    void knapsack(){
        W[0,1,2,...,C]=0;
        for(i=1;i<=n;i++){
            for(j=c;j>=1;j--){
                if(si<=j){
                    W[j]=max{W[j],W[j-si]+vi};
                }
            }
        }
        return W[C];
    }
    

第八章复习

贪心策略

  • 每一步都建立在局部最优解的基础上;
  • 不一定可以获得全局最优解;
  • 有些问题可以获得全局最优解:比如:最短路径;最小生成树;

最短路径

  • 问题:G=(V,E)有向带权图;

  • Dijksra算法:

    • 算法框架:
    1. X ← \leftarrow {1};Y ← \leftarrow V -{1};
    2. 对每个 v ∈ Y v\in Y vY,如果存在从1到v的边,则令 λ [ v ] λ[v] λ[v] (v的标记)为边的长度;否则令 λ [ v ] = ∞ λ[v] =∞ λ[v]=,并设 λ [ 1 ] = 0 λ[1]=0 λ[1]=0;
    3. while Y ≠ \neq = {}
    4. y ∈ Y y \in Y yY,使得 λ [ y ] λ[y] λ[y]为最小;
    5. 将y从Y移到X;
    6. 更新那些在Y中与y相邻的顶点的标记
    7. end while;
    • 算法呈现:
    X={1};Y=V-{1};λ[1]=0;
    for(y=2;y<=n;y++){
        if(y相邻于1){
            λ[y]=length[1,y];
        }
        else λ[y]=;
    }
    for(j=1;j<=n-1;j++){
        令y∈Y,使得λ[y]为最小
        X=X∪{y} //{将顶点y加入X}
       	Y=Y∪{y}	//{将顶点y从Y中删除}
        for(每条边y,w){
            if(w∈Y && λ[y]+length[y,w]<λ[w]){
                λ[w]=λ[y]+length[y,w];
            }
        }
    }
    

最小生成树

  • 问题:G=(V,E)无向联通带权图;求(V,T),其中T是E的子集;

可以回顾前面所讲的树相关的知识;

  • Kruskal算法:

    • 判断加入某条边,是否会导致回路:
      • 思路:将节点分成若干个子集,已联通的节点放在同一子集里
        • 并集操作
        • 判定是否属于相同子集;
    • MAKESET,FIND(x),UNION(y);
    • 每次FIND和UNION的时间复杂度 O log ⁡ n O\log n Ologn;
    • 算法框架:
    //按非降序权重将E中的边排序
    for 每条边v∈V
        Makeset({v});
    end for
    T={}
    while |T|<n-1(x,y)为E的下一条边
        if FIND(x) != FIND(y)(x,y)加入T
            UNION(x,y);
    	end if
    end while
    
    • 算法分析:时间复杂度 O ( n 2 ∗ log ⁡ n ) O(n^2 * \log n) O(n2logn);
  • Prim算法:

    • 算法框架:
    T={};X={1};Y=V-{1};
    while Y !={}(x,y)是最小权重的边,其中x∈X,y∈Y
        X=X∪{y};
    	Y=Y-{y};
    	T=T∪{(x,y)};
    end while
    
    • 算法的实现:

      • 数组N[1,2,…,n]和C[1,2,…,n],C[y]表示边(y,N[y])的权值;
      • 算法步骤一:初始化
      T={};X={1};Y=V-{1};
      for(y=2;y<=n;y++){
          if(y邻接于1){
          	N[y]=1;
              C[y]=c[1,y];
          }
          else C[y]=;
      }
      
      • 算法步骤二:
      for(j=1;j<=n-1;j++){
          令y∈Y,使得C[y]最小
          T=T∪{(y,N[y])};
          X=X∪{y};
          Y=Y-{y};
          for(每个邻接于y的顶点w∈Y){
              if(c[y,w]<C[w]){
                  N[w]=y;
                  C[w]=c[y,w];
              }
          }
      }
      
    • 算法分析:时间复杂度 O ( n 2 ) O(n^2) O(n2);

第九章复习

广度优先和深度优先这里不在赘述;

第十章复习

  • 易解问题与难解问题的主要区别
    • 多项式函数与指数函数的增长率有本质区别;
    • 计算机性能的提高对易解问题与难解问题算法的影响;
    • 多项式时间复杂性忽略了系数,不影响易解问题与难解问题的划分;

P类问题和NP类问题

  • 划分标准是基于对所谓判定问题的求解方式

  • 对判定问题求解,可以采用确定性算法:

    • 定义一:设A是求解问题Π的一个算法,如果在算法的整个执行过程中,每一步只有一个确定的选择,则称算法A是确定性算法

      特点:对同一输入实例,运行算法A,所得结果是一样的

    • 定义二(P类问题):如果对于某个判定问题Π,存在一个非负整数k,对于输入规模为n的实例,能够以 O ( n k ) O(n^k) O(nk)的时间运行一个确定性算法,得到yes或no的答案,则称该判定问题Π是一个P(Polynomial)类问题;

    • 定义三(非确定性算法):设A是求解问题Π的一个算法,如果算法A以如下猜测+验证的方式工作,则称A为非确定性(nondeterminism)算法:

      • 猜测阶段:对问题的输入实例产生一个任意子串y,在算法的每次运行,y可能不同,因此猜测是以非确定的形式工作,这个工作一般可以在线性时间内完成
      • 验证阶段:在这个阶段,用一个确定性算法验证两件事:首先验证猜测的y是否是合适的形式,若不是,则算法停下并回答no;若是合适形式,则继续检查它是否是问题x的解,如果确实是x的解,则停下来并回答yes,否则停下来回答no,要求验证阶段在多项式时间内完成。
    • 注意对非确定性算法输出yes/no的理解:

      • 若输出No,并不意味着不存在一个满足要求的解,因为猜测可能不正确;若输出yes,则意味着对于该判定问题的某一输入实例,至少存在一个满足要求的解。
    • 定义四(NP类问题):如果对于判定问题Π,存在一个非负整数k,对于输入规模为n的实例,能够以 O ( n k ) O(n^k) O(nk)的时间运行一个非确定性算法,得到yes/no的答案,则该判断问题Π是一个NP(nondeterminism Polynomial)类问题,通俗的说:多项式时间内可以验证某个值是否是正确答案。

      注意:NP类问题是对于判定问题定义的,事实上,可以在多项式时间内应用非确定性算法解决的所有问题都属于NP类问题;

  • 关于P与NP关系的初步思考:

    • P ⊆ N P P\subseteq NP PNP

    • 若问题Π属于NP类,则存在一个多项式时间的非确定性算法,来猜测并且验证它的解;但不一定能够构造一个多项式时间的确定性算法,来对它进行求解或判定

      因此,人们猜测 P ≠ N P P\neq NP P=NP,但是否成立,至今未得到证明。

NP完全问题

  • NP完全问题是NP类问题的子类,一个具有特殊性质与特殊意义的子类;

  • 问题归约:NP类问题在最坏情况下的时间复杂性一般都是快速增长的指数函数,我们希望能够在NP类问题内部找到一种方法,比较两个问题的复杂性。

  • 定义五(多项式时间归约):若在 O ( τ ( n ) ) O(τ(n)) O(τ(n))时间内完成上述输入/输出转换,则称问题Π以 τ ( n ) τ(n) τ(n)时间归约到问题Π’,记为 Π ∝ τ ( n ) Π ′ Π∝τ(n)Π' Πτ(n)Π,其中,n为问题规模;若在多项式时间内完成上述转换,则称问题Π以多项式时间归约到问题Π’,记为 Π ∝ p o l y Π ′ Π∝polyΠ' ΠpolyΠ

  • 举例(多项式归约):

    • 排序问题的算法A

    • 配对问题Π:

    • 求解配对问题,需要进行三个变换:

      • 将配对问题的输入x,y变成排序问题的两个输入I1,I2;
      • 应用算法A对I1,I2分别排序,得到两个排序输出O1,O2;
      • 将两个排序输出O1,O2,转换成配对问题的输出O;
  • 多项式时间归约的性质:

    • 传递性
    • 自反性
  • NP完全问题是一类具备如下特殊性质的NP类问题:

    • Π(该问题本身)就是一个NP类问题;
    • 每个NP类问题都可以多项式时间归约为Π;
  • 定义六(NP完全问题):令Π是一个判定问题,如果问题Π属于NP类问题,并且对NP类问题中每一个问题Π’,都有 Π ∝ p o l y Π ′ Π∝polyΠ' ΠpolyΠ,则称判定问题Π是一个NP类完全问题。

    • NP完全问题是NP类问题中最难的一类问题;
    • 如果某一个NP完全问题能在多项式时间内解决,则每一个NP完全问题都能在多项式时间内解决;
    • 这些问题也许存在多项式时间算法,也可能不存在多项式时间算法;
  • 如何证明一个问题是NP完全问题

    • 证明问题Π是NP类问题
    • 证明NP类问题中的每一个问题都能在多项式时间内变换为问题Π
  • 已经证明的NP完全问题:SAT问题,最大团问题,图着色问题,哈密顿回路问题,旅行商问题,背包问题,最长路径问题,扫雷游戏····

NP难问题

  • 难解问题中还有一类问题,虽然也能证明所有NP类问题,可以在多项式时间内变换到问题Π,但并不能证明Π也是NP类问题,所以Π不是NP完全的,但问题Π至少与任意NP类问题有同样的难度,这样的问题是NP难问题

NPI类问题

  • NP类问题中还有一些问题,人们不知道是属于P类还是输于NP完全问题,还有待于证明其归属;
  • 这些问题是NP完全问题的可能性非常小,也因为不相信他们在P中,我们人为地增加另一问题类来接纳这类问题,这个类被称为NPI类
  • NPI类是一个人为定义的,动态的概念,随着人们对问题研究的深入,许多NPI类问题逐渐被明白无误地证明他们原本属于P类问题或NP类完全问题
    • 例如:线性规划问题,素数判定问题

NP完全问题的处理技术简介

NP完全问题是计算机难以处理的,但是实际中经常遇到,因此人们提出了解决完全问题的各种方法:

  • 采用先进的算法设计技术
  • 近似算法
  • 随机算法
  • 并行计算
  • 智能算法

十三章复习

回溯法

  • 有组织的穷尽搜索

    • 有明确的的适用对象
    • 有明确的的设计步骤
  • 例:着色问题

    • V={v1,v2,…,vn}
    • ci={1,2,3} //颜色集合
    • 每种着色方法:(c1,c2,…,cn);
    • 递归算法:
    for(k=1;k<=n;k++){
        c[k]=0;
    }
    flag = false;
    graphcolor(1);
    if(flag){
        output c;
    }
    else output"no solution";
    
    void graphcolor(k){
        for(color=1;color<=3;color++){
            c[k]=color;
            if(c为合法着色){
                set flag<-true
                exit;
            }
            else{
                if(c是部分着色){
                	graphcolor(k+1);
                }
            }
        }
    }
    
    • 迭代算法:
    for(k=1;k<=n;k++){
        c[k]=0;
    }
    flag =false;
    k=1;
    while(k>=1){
        while(c[k]<=2){
            c[k]=c[k]+1;
            if(c为合法着色){
            	flag=true;
                exit;
            }
            else{
                if(c为部分着色){
                    k=k+1;
                }
            }
        }
        c[k]=0;
        k=k-1;
    }
    if(flag){
        output c;
    }
    else output"no solution";
    
  • n皇后问题:

    • 当n=4时:

      • 解的表示形式{x1,x2,x3,x4};
      • 互相攻击的情况:
        • xi=xj;
        • xi-xj=i-j;或者xi-xj=j-i;
    • 递归算法:c[1,…,4]表示存放位置

      • 主程序:
      for(k=1 to 4) c[k]=0;
      flag =false;
      Rec4-queens(1);
      if(flag= true)	 output c[];
      else output"no solution"
      
      Rec4-queens(k){
          for(col=1 to 4){
              c[k]=col;
              if(c全合法){
                  flag = true;
                  exit;
              }
              else{
                  if(c部分合法){
                  	Rec4-queens(k+1);
                  }
              }
          }
      }
      
    • 迭代递归的算法:

      • 算法过程;
      for(k=1 to 4) c[k]=0;
      flag = false;
      k=1;
      while(k>=1){
          while(c[k]<=3){
              c[k]=k+1;
               if(c全合法){
                  flag = true;
                  exit;
              }
              else{
                  if(c部分合法){
                      k=k+1;
                  }
              }
          }
          c[k]=0;
          k=k-1;
      }
      if(flag= true)	 output c[];
      else output"no solution"
      
    • 简要分析:

      • 蛮力分析: O ( n n ) O(n^n) O(nn)或者 O ( n ! ) O(n!) O(n!)
      • 回溯法:经验上的期望运行时间少
  • 回溯法适用于解决什么类型问题?

    • 解的形式是定长向量
    • 算法框架:
    初始化 c[n];
    flag = false;
    回溯过程:递归/迭代
    if(flag= true)	 output c[];
    else output"no solution"
    
    • 递归回溯:
    advance(k){
        for(每个x∈Xk){
            c[k]=x;
            if(c合法){
                flag=true;
                exit;
            }
           	else{
                if(c部分合法){
    				advance(k+1);            
                }
            }
        }
    }
    
    • 迭代回溯:
    k=1;
    while(k>=1){
        while(Xk尚未被穷举){
            c[k]=Xk的下一个元素;
             if(c全合法){
                flag = true;
                exit;
            }
            else{
                if(c部分合法){
                    k=k+1;
                }
            }
        }
        重置c[k];
        k=k-1;
    }
    

第十四章复习

随机算法的分类

  • 数值计算:pi的计算;

    • 算法呈现
    double Darts(int n){
        int k=0;
        for(int i=1;i<=n;i++){
            double x = Random(-1,1);
            double y = Random(-1,1);
            if((x*x+y*y)<=1){
                k++;
            }
        }
        return 4*k/(double)n;
    }
    
  • 拉斯维加斯算法:回溯法;

    • n皇后问题
  • 蒙特卡罗算法:多数元,素性测试;

    • 素性测试
  • 舍伍德算法:快排,元素选择;

    • 快排算法呈现:
    rquicksort(low,high){
        if(low<high){
            v=random(low,high);
            互换A[low]和A[v];
            SPILT(A[low···high],w);
            rquicksort(low,w-1);
            rquicksort(w+1,high);
        }
    }
    
  • 随机算法的分析:每个固定实例上的期望运行时间;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值