zoukankan      html  css  js  c++  java
  • 算法期末备考-第1练-分支界限法

    算法期末备考-第1练 

    考虑到 大家针对备考 算法无从下手。

    同时算法是最后一门考试科目,可能复习比较匆忙就考试了。

    从今天开始每天进行一练,希望大家每天花上至少一个小时来复习,只要大家重视起这门课,就不会挂科。

     

    算法是以理解为基础。

    “理解是最好的记忆”

    不要背代码,不要背代码,不要背代码。

    等你理解算法核心后,做题和复习起来才会胸有成竹

     

    内容主要分为

    1、掌握算法基本思路

    2、历年真题

    3、课后习题

     

    如果发现代码有错误或者有疑问请及时联系我。

    放在博客上,主要是因为比较容易修改。

     

    不用光顾着看,大脑非常容易欺骗自己,大家必须动手实践一下。

    道理就好比记单词。

     

    建议:

    通过热身环节后,大家自己试着写一下后面的题目。

    如果遇到不会才看参考代码,千万不要先入为主直接背代码,这样效率低而且容易忘。

     

    热身环节 

    子集树问题

     

    题目描述:

      给定3个元素的一个集合,输出对应的所有子集的情况。

      3个元素构成的子集为2^3=8种情况。

      分别为“111,110,101,100,011,010,001,000”

    题解:

      面对子集的问题有很多种解法,其中一种方法为BFS,对于这颗搜索树来说是层次遍历。

      顺带借助子集树问题来介绍一下BFS算法。

      

      介绍

      BFS 是 Breadth first Search 的首字母组合。

      中文翻译回来是“宽度优先搜索”

      在这个题目中就相当于"层次遍历",一层一层地遍历,其字母顺序为:“A,BC,DFGH,IJKLMNO”。

      算法过程

      过程中利用到了数据结构“队列”利用其先进先出的特性。

      请大家拿出草稿纸模拟其中过程。

      1、把根节点A放入队列。

      2、弹出队首结点,同时将  队首结点的左右两个结点依次放入队列。

      3、重复第二步就能实现层次遍历。

      1、A

      2、“A”   |  'BC'

      3、“B”   |  C'DE'

      4、“C”   |  DE'FG'

      ……

      图示:

      弹出队首用“”表示。

      新加入队列的元素用''表示。

      " | " 后面的是当前队列中的元素。

     


      前置知识:

      过程中需要用到队列,在此之前必须要知道如何定义队列中元素的属性。

      “属性”,其实根据题目来说的,

      通常根据题目进行加属性。

      如果是搜索树,以树为结构的必定要有一个变量作为层数   我常用Step来表示。

      如迷宫类问题,必须有用于定位的坐标"int x,y"。

      如果是背包问题,每个结点表示都是一种状态,每个背包都有自身的价值和重量。“int 价值:val,重量:w”

      如果是整数变换问题,每个结点都有各自的值。“int x”

      如果题目需要记录对应的路径,则定义“ char path[N]”

    typedef struct Node{
        int step ;
        char path[N];
    }Node;

      然后是C++库中提供的STL库中的queue

    #include<queue>         //对应的头文件
    using namespace std;    //利用STL库一定记住要写上"命名空间"
    
    <queue>
    
    //定义
    queue<Node>Q;           //定义存放Node类型队列,其名为"Q"
    
    //库函数
    Q.push( (Node)*** );    //把Node类型变量,插入到Q的队尾
    Node t = Q.front();     //将队首元素进行赋值给Node类型的变量t
    Q.pop();                //弹出队首元素
    Q.empty();              //判断当前的队列中是否为空? 返回bool类型 , 若是真的为空,返回true,否则返回 false

      算法实现的大体思路:

      将队列中队首结点拿出来,判断其结点是否为叶子结点。

        1、如果为叶子结点,输出叶子结点中的路径

        2、否则,把自己与其相连的两个孩子结点给插入到队列后面。

      然后具体套路模版上课已经讲过,如果忘记了的同学可以看看下面的代码复习一下具体过程。

    代码链接:https://pasteme.cn/25521

     

     1 //子集树
     2  3 #include<queue>
     4 #include<cstdio>
     5 using namespace std;
     6  7 //定义结点记录信息
     8 typedef struct Node{
     9     char path[6];
    10     int step ;
    11 }Node;
    12 13 // BFS套路
    14 void BFS(){
    15 16     //第一步:定义根节点和队列,并把根节点压入队列
    17     queue<Node> Q ;
    18     Node first ;
    19     first.step = 0 ;
    20     Q.push(first) ;
    21 22     //第二步:保持队列非空
    23     while( !Q.empty() ){
    24 25         //第三步:取出队首元素,别忘了同时要弹出队首元素
    26         Node cur = Q.front() ;
    27         Q.pop() ;
    28 29         //第四步:设计 最终状态 进行的操作
    30         if( cur.step == 3 ){
    31             for( int i = 1 ; i <= 3 ; i ++ ){
    32                 printf("%c",cur.path[i]);
    33             }
    34             putchar('
    ');
    35             continue ;
    36         }
    37 38         //第五步:设计 下一步怎么走,用"Next"来命名下一步结点,压入队尾
    39 40         Node Next = cur ;
    41         Next.step += 1 ;
    42 43         //走左边
    44         Next.path[ cur.step + 1 ] = 'L' ;
    45         Q.push( Next );
    46 47         //走右边
    48         Next.path[ cur.step + 1 ] = 'R' ;
    49         Q.push( Next );
    50     }
    51 }
    52 53 int main()
    54 {
    55     BFS();
    56     return 0;
    57 }
    子集树问题

     


     

    历年真题

    01背包问题

    题目描述:

      有一个背包容量为30;同时有三个物品<value,weight>为<45,16>,<25,15>,<25,15>

      请问,对于背包能承受的最大重量来说,背包的最大价值为多少?

    题解:

      以子集树为基础,每个结点只是多了价值和重量。

      但是非叶子结点在放入物品时必须必须要加上限制条件,必须是背包能放入的情况下才插入队尾。

    代码链接:https://pasteme.cn/25528

     

     1 //01背包问题
     2  3 #include<queue>
     4 #include<cstdio>
     5 #include<algorithm>
     6 using namespace std;
     7  8 //定义结点记录信息
     9 typedef struct Node{
    10     int val , w ;
    11     int step ;
    12 }Node;
    13 14 int weight[3] = { 16 , 15 , 15 };
    15 int value[3] = { 45 , 25 , 25 };
    16 int V = 30 ;
    17 18 // BFS套路
    19 void BFS(){
    20 21     int n = 3 ;     // 物品个数
    22     int ans = 0 ;   // 答案
    23 24     //第一步:定义根节点和队列,并把根节点压入队列
    25     queue<Node> Q ;
    26     Node first ;
    27     first.step = first.val = first.w = 0 ;
    28     Q.push(first) ;
    29 30     //第二步:保持队列非空
    31     while( !Q.empty() ){
    32 33         //第三步:取出队首元素,别忘了同时要弹出队首元素
    34         Node cur = Q.front() , Next ;
    35         Q.pop() ;
    36 37         //第四步:设计 最终状态 进行的操作 <=> 到达叶子结点.
    38         if( cur.step ==  n ){
    39             ans = max( ans , cur.val );
    40             continue ;
    41         }
    42 43         //第五步:设计 下一步怎么走,用"Next"来命名下一步结点,压入队尾
    44         Next = cur ;
    45         Next.step += 1 ;
    46 47         //该操作为:物品不放进背包 <=> 子集树往左边走
    48         Q.push( Next );
    49 50         //该操作为:物品放入背包 <=> 子集树往右边走
    51         //前提是:保证物品能放入背包
    52         if( Next.w + weight[ cur.step ] <= V ){
    53             Next.w += weight[ cur.step ];
    54             Next.val += value[ cur.step ];
    55             Q.push( Next ) ;
    56         }
    57     }
    58     printf("%d
    ",ans);
    59 }
    60 61 int main()
    62 {
    63     BFS();
    64     return 0;
    65 }
    01背包问题

     

    布线问题

    题目描述:

      给定一个迷宫,其中白色为路,灰色为墙,每移动一格就说明距离加一,给定起点和终点。

      请问 a->b的最短距离为多少?

    题解:

      对于这类迷宫题目,记住

      定义结点是{x,y,step}的三元组,maze[][]迷宫路和墙的情况,vis[][]迷宫是否被访问,dir[4][2]方向数组

      向四个方向遍历时必须也要做到三个判断:

        1、不在边界,即(x,y)要合法

        2、maze[x][y]必须为路,如果时墙就走不过去。

        3、vis[x][y]看看x,y是否访问过。

      注意细节,然后到达终点提前结束即可。

             

    代码链接:https://pasteme.cn/25532

     

      1 //布线问题
      2   3 #include<queue>
      4 #include<cstdio>
      5 #include<algorithm>
      6 using namespace std;
      7   8 //定义结点记录信息
      9 typedef struct Node{
     10     int x , y ;
     11     int step ;
     12 }Node;
     13  14 int dir[4][2] = {
     15         {-1,0},
     16     {0,-1} , {0,1},
     17         {1,0}
     18 };
     19  20 int maze[9][9] ;
     21 int vis[9][9] ;
     22 int n = 9 , m = 9 ;
     23 int Sx,Sy , Ex,Ey ; //Start( x , y ) , End( x ,y )
     24  25 void Init(){
     26     maze[1][3] = maze[2][3] = maze[2][4] =
     27     maze[3][5] = maze[4][4] = maze[4][5] =
     28     maze[5][5] = maze[6][1] = maze[7][1] =
     29     maze[7][2] = maze[7][3] = maze[8][1] =
     30     maze[8][2] = maze[8][3] = 1;
     31  32     vis[1][3] = vis[2][3] = vis[2][4] =
     33     vis[3][5] = vis[4][4] = vis[4][5] =
     34     vis[5][5] = vis[6][1] = vis[7][1] =
     35     vis[7][2] = vis[7][3] = vis[8][1] =
     36     vis[8][2] = vis[8][3] = -1;
     37     Sx = 3 , Sy = 2 ;
     38     Ex = 4 , Ey = 7 ;
     39  40     //起点特定标记
     41     vis[Sx][Sy] = -1 ;
     42 }
     43  44 // BFS套路
     45 void BFS(){
     46  47     //是否能到达终点 的标记位
     48     bool flag = false ;
     49  50     //第一步:定义根节点和队列,并把根节点压入队列
     51     queue<Node> Q ;
     52     Node first = Node{ Sx , Sy , 0 } ;
     53     Q.push(first) ;
     54  55     //第二步:保持队列非空
     56     while( !Q.empty() ){
     57  58         //第三步:取出队首元素,别忘了同时要弹出队首元素
     59         Node cur = Q.front() , Next ;
     60         Q.pop() ;
     61  62         //第四步:设计 最终状态 进行的操作
     63         if( cur.x ==  Ex && cur.y == Ey ){
     64             printf("The Shortest Path : %d
    ",cur.step ) ;
     65             flag = true ;
     66             break ;
     67         }
     68  69         //第五步:设计 下一步怎么走,用"Next"来命名下一步结点,压入队尾
     70  71         for( int i = 0 ; i < 4 ; i++ ){
     72             int tx = cur.x + dir[i][0] ;
     73             int ty = cur.y + dir[i][1] ;
     74             if( ( 1 <= tx && tx <= n && 1 <= ty && ty <= m ) && maze[tx][ty] == 0 ){
     75                 if( vis[tx][ty] == 0 ){
     76                     vis[tx][ty] = cur.step + 1  ;
     77                     Next = Node{ tx , ty , cur.step + 1 } ;
     78                     Q.push( Next );
     79                 }
     80             }
     81         }
     82     }
     83     // 如果没有到达终点输出"Impossible"
     84     if( !flag ){
     85         printf("Impossible
    ");
     86     }
     87     // 输出BFS遍历路径
     88     else{
     89         for( int i = 1 ; i <= n; i ++ ){
     90             for( int j = 1 ; j <= m ; j++ ){
     91                 printf("%4d",vis[i][j]);
     92             }
     93             putchar('
    ');
     94         }
     95         putchar('
    ');
     96     }
     97 }
     98  99 int main()
    100 {
    101     Init();
    102     BFS();
    103     return 0;
    104 }
    布线问题

     

    课后习题

    整数变换问题

    题目描述:

    关于整数i的变换f和g定义如下:f(i)=3i,g(i)=i/2
    试设计一个算法,对于给定的2个整数n和m,用最少的变换次数将n变成m。
    输入:
    15 4 输出: 4 ggfg

    题解:

      其实这个问题跟子集树类似,问题虽然不知道答案落在哪一层。

      但是通过二叉树层次遍历即可,左边是:f()变换,右边是:g()变换

      只要遇到答案即可提前结束程序,注意该题目还需要加上路径记录。

     代码链接:https://pasteme.cn/25533

     1 //整数变换问题
     2 //关于整数i的变换f和g定义如下:f(i)=3*i,g(i)= i/2.
     3 //试设计一个算法,对于给定的2个整数n和m,用最少的变换次数将n变成m。
     4  5 #include<queue>
     6 #include<cstdio>
     7 #include<algorithm>
     8 using namespace std;
     9 10 //定义结点记录信息
    11 typedef struct Node{
    12     int x , step ;
    13     char path[30];
    14 }Node;
    15 16 int S = 15 , E = 4 ;
    17 void BFS( int S ){ // Start
    18 19     queue<Node> Q;
    20     Node first ;
    21     first.x = S ;
    22     first.step = 0 ;
    23     Q.push(first) ;
    24 25     while( !Q.empty() ){
    26         Node cur = Q.front() , Next ;
    27         Q.pop() ;
    28 29         if( cur.x == E ){
    30             printf("最少变换的次数: %d
    ",cur.step );
    31 32             //输出变化的过程
    33             for( int i = 0 ; i < cur.step ; i ++ )
    34                 printf("%c",cur.path[i]);
    35             putchar('
    ');
    36             break ;
    37         }
    38 39         Next = cur ;
    40         Next.x = cur.x * 3 ;
    41         Next.step += 1 ;
    42         Next.path[cur.step] = 'f' ;
    43         Q.push( Next );
    44 45         Next = cur ;
    46         Next.x = cur.x / 2 ;
    47         Next.step += 1 ;
    48         Next.path[cur.step] = 'g' ;
    49         Q.push( Next );
    50     }
    51 }
    52 53 int main()
    54 {
    55     BFS(S);
    56     return 0;
    57 }
    58  
    整数变换问题
     
     
  • 相关阅读:
    模拟电梯运行
    用户需求调研报告
    NABC需求分析
    大道至简---读书随笔3
    二维环形数组求最大子数组和
    结对开发之求最大数组溢出问题
    结对开发之环形数组
    结对开发之电梯调度
    我看“微软拼音”
    团队开发项目之典型用户和用户场景
  • 原文地址:https://www.cnblogs.com/Osea/p/12106718.html
Copyright © 2011-2022 走看看