算法期末备考-第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('\n');
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\n",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\n",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\n");
 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('\n');
 94         }
 95         putchar('\n');
 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\n",cur.step );
31 32             //输出变化的过程
33             for( int i = 0 ; i < cur.step ; i ++ )
34                 printf("%c",cur.path[i]);
35             putchar('\n');
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  
整数变换问题
 
 
posted @ 2019-12-27 13:00  Osea  阅读(1581)  评论(0编辑  收藏  举报