算法设计备考笔记

第一章 算法概述

  1. 算法的定义:算法是解决问题的过程

  2. 算法的三要素:操作,控制结构,数据结构

  3. 算法的五大基本特征:

    • 有穷性
    • 确定性
    • 可行性
    • 零个或多个输入
    • 一个或多个输出
  4. 算法的复杂性:时间复杂度&空间复杂度

  5. 时间复杂度的衡量方法:事前分析估算法,事后分析法

  6. 常数级-阶乘级,至顶向下复杂度越来越大


    IMG_20211221_234601.jpg
  7. 大O表示法中的大O是复杂度的上界

第二章 迭代与蛮力

  1. 迭代法的定义:是一种用旧值推出新值的算法

  2. 迭代法的实现步骤:

    1. 确定迭代模型
    2. 确定迭代关系式
    3. 对迭代过程进行控制
  3. 迭代法有两种:

    • 正推法(递推法)
    • 倒推法
  4. 正推法的示例:兔子繁殖

  5. 倒推法:猴子吃桃

  6. 蛮力法定义:枚举法(穷举法)是蛮力法的一种表现形式,它是根据问题中的条件将可能的情况一一列举出来,逐步尝试求解

  7. 蛮力法的步骤:

    1. 找出枚举范围
    2. 找出约束条件
  8. 蛮力法的应用:百钱百鸡

  9. 百钱百鸡算法:

    for(int x = 1; x <= 20; x++){ // 公鸡
        for(int y = 1; y<= 33; y++){ // 母鸡
            int z = 100 - x - y; // 小鸡
            if((z % 3 == 0) && (x * 5 + y * 3 + z / 3 == 100))  //核心
        }
    }
    

第三章 分治法

  1. 分治法是用递归实现的

  2. 分治法的定义:将一个难以直接解决的大问题分割为几个规模较小的相似问题,再逐个击破

  3. 分治法的步骤:

    1. 分解
    2. 解决
    3. 合并
  4. 适用于分治法的问题所应具备的特征:

    1. 问题能够被分解,且分解后最好相互独立
    2. 每个小问题都是相似的
    3. 合并之后能够推出原问题的解
  5. 分治法的应用:金砖问题

  6. 金砖问题算法:

    float a[n];
    maxmin(int i, int j, float & fmax, float & fmin){
        int mid; 
        float lmax, lmin, rmax, rmin;
        if (i=j){
            fmax=a[i];
            fmin=a[i];
        }else if (i=j-1){
            if(a[i] < a[j]){
                fmax = a[j];
                fmin = a[i];
            }else{
                fmax = a[i];
                fmin = a[j];
            }
        }else{
            mid = (i+j)/2;       //核心
            maxmin(i, mid ,lmax,lmin);
            maxmin(i, mid, lmax, lmin);      //核心
            if(lmax>rmax)
                fmax = lmax;
            else
                fmax = rmax;
            if(lmin>rmin)
                fmin = rmin;
            else
                fmin = lmin;
        }
     
        
    }
    
  7. 最大子段和问题是用分治法解决的

第四章 贪心算法

  1. 贪心算法又叫贪婪算法,登山算法
  2. 贪心算法的根本思想以逐步的局部最优,达到最终的全局最优
  3. 选择贪心算法需要具有无后向性
  4. 贪心算法的应用:付款问题,币种统计问题,取数游戏问题
  5. 贪心算法的基本思想:从问题的某一个初始解出发,逐步逼近给定的目标,每一步都做一个不可回溯的决策,尽可能的求出最好的解,当达到某算法中的某一步,不需要再前进时,算法停止

第五章 动态规划

  1. 动态规划的基本思想(动态规划和分治法的异同点):动态规划算法与分治算法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解,与分治法不同的是,适合用于动态规划求解的问题,经分解得到的子问题,往往不是互相独立的,若用分治法来解决这类问题,则分解得到的子问题数目太多,有些子问题被重复计算了很多次

  2. 最长公共子序列问题是使用动态规划来解决的

  3. 最大子段和不仅可以用动态规划来解决,也可以用分治法解决

  4. 仅需知道对于p(i,j)或者p(k,w)都是指i的最优值

2.png
  1. 递推关系式的推导方法:(掌握如何推导)
1.png
  1. 求解最优值

    int w[4] = {2,3,4,5};
    int v[4] = {3,4,5,6};
    for(int i=1; i<=n; i++){
        for(int j=1;j<=c,j++){
            if(w[i-1]>j){        //核心代码
                p[i][j] = p[i-1][j];
            }else{
                p[i][j] = max(p[i-1][j-w[i-1]]+v[i-1],p[i-1][j]);
            }
        }
    }
    
  2. 求解最优解

    int isp[n];
    j = c;
    for (i = n; i > 0; i--){
        if(p[i][j]>p[i-1][j]){       //核心代码
            isp[i] = 1;
            j = j - w[i-1];
        }else{
            isp[i] = 0;
        }
    }
    
  1. 矩阵链式方程-状态转移方程

    m(i,j)代表最小相乘次数,熟记状态转移方程

image-20211222013118943-16401078819418.png

第六章 图的搜索

  1. 图的定义:顶点的集合以及边的集合,顶点不能为空,边不能为空

  2. 图的搜索办法:

    • 广度优先
    • 深度优先
  3. 深度优先搜索的主要思想:

    首先以一个未被访问过的顶点作为起始顶点,延当前顶点的边走到为访问过的顶点,当没有未访问的顶点时,则回到上一个顶点,继续试探访问别的顶点,直到所有的顶点都被访问

  4. 广度优先搜索的主要思想

    从根节点开始,沿着树的宽度遍历树的结点

第七章 回溯法

  1. 回溯的基本做法是搜索(深度优先)

  2. 回溯法的步骤:从根节点出发搜索解空间树

  3. 回溯法的基本思想:

    回溯法在问题的解空间树中,按深度优先策略,从根结点出发搜索解空间树,算法搜索至解空间树的任意结点时,先判断该结点是否包含问题的解,如果肯定不包含,则跳过以该结点为根的子树的搜索,逐层向其祖先节点回溯,否则进入该子树,继续按深度优先策略搜索,回溯法计算问题的所有解时,要回溯到根,且根结点的所有子树都已经被搜索完成才结束

  4. 需要强调的是,问题的解空间树是虚拟的,并不需要在算法运行时构造一棵真正的树结构

  5. 回溯法属于蛮力穷举法,时间复杂度不会太好或者太差,在最坏情况下,时间代价肯定为指数阶

  6. 在n皇后问题的可能解中,对应的解空间树有n!个叶子结点,每个叶子结点代表一种可能解,在n=4的情况下,解空间树有65个结点,24个叶子结点

  7. 虽然解空间树共有65个结点,24个叶子结点,但在实际搜索过程中,遍历操作只涉及27个结点就找到了满足条件的解,原因在于剪枝函数

  8. 图的着色问题是使用回溯法解决的

  9. 旅行商问题是使用回溯法解决的

  10. 教材P218-P219页4,5,6,7,8会考察,尤其关注4,5

    1. 第四题:n皇后问题:

      #include 
      const int N=10; 
      int a[N]; 
      int count,n;   
       
       
      bool check(int x,int y){  
       for(int i=1;i<=x;i++){
           if(a[i]==y) return false;   
           if(i+a[i]==x+y) return false;  
           if(i-a[i]==x-y) return false;  
       } 
       return true; 
      }
      Print(int n){
       count++;   
       printf("第%d种解",count);
       for(int i=1;i<=n;i++){
           printf("(%d,%d)",i,a[i]);
       } 
       printf("\n");
      }
       
      
      void dfs(int row){
       if(row>n) {  
           Print(n); 
           return ;
       }
       for(int i=1;i<=n;i++){
           if (check(row,i)){  
               a[row]=i; 
               dfs(row+1);  
               a[row]=0; 
           }
       }
      } 
      int main(){
       printf("输入n*n的棋盘数:"); 
       scanf("%d",&n);
       dfs(1);  
       printf("一共有%d种解",count);
       return 0; 
      }
      
    2. 第五题:求子集问题:

      #include
      using namespace std;
       
      #define NUM 10000
      int n;
      int c;
      int cw;
      int bestw;
      int w[NUM];
      int x[NUM];
      int r;
      bool flag;
       
      void backtrack(int t)
      {
       if(t>n)
       {
           if(cw==c)
           {
               for(int i=1; i<=n; i++)
                   if (x[i]) printf("%d ",w[i]);
               printf("\n");
               flag = false;
           }
           return;
       }
       r -= w[t];
       if (cw+w[t]<=c)
       {
           x[t] = 1;
           cw += w[t];
           backtrack(t+1);
           cw -= w[t];
       }
       if (cw+r>bestw)
       {
           x[t] = 0;
           backtrack(t+1);
       }
       r += w[t];
      }
       
      int main()
      {
       while(scanf("%d%d",&n,&c) && (n||c))
       {
           r = 0;
           for(int i=1; i<=n; i++)
           {
               scanf("%d", &w[i]);
               r += w[i];
           }
           cw = 0;
           bestw = 0;
           flag = true;
           backtrack(1);
           if (flag) printf("No Solution!\n");
       }
       return 0;
      }
      

痛斥一切认为理所应当的坐享其成者!!!

你可能感兴趣的:(算法设计备考笔记)