蓝桥杯_搜索算法(DFS和BFS)

蓝桥杯dfs深度优先搜索之凑算式、排列组合、图连通

2015年(第六届)蓝桥杯B组省赛
水题 1(3分)2(5分)4(11分) 19分
DFS/爆破 3(9分)5(15分)7(21分)45分
冒泡(加法乘法)6(17分)17分
取余(饮料换购)8(13分)13分
矩阵 9(25分) 25分
DP(动态规划) 10(31分)31分
2016年(第七届)蓝桥杯B组省赛
递推 1(3分)、2(5分) 8分
函数调用 4(11分) 11分
DFS/爆破 3(9分)、5(13分)、6(15分)、7(19分)、8(21分)77分
DP 9(23分) 23分
数据结构 10(31分) 31分
2017年(第八届)蓝桥杯B组省赛
分类 题号 总分
文件处理 1(5分)3(13分)18分
DFS/爆破 2(11分)4(17分)28分
水题 5(7分) 7分
DP 6(9分)8(21分)30分
日期问题 7(19分)19分
二分问题 9(23分)23分
前缀和 10(25分)25分
2018年(第九届)蓝桥杯B组省赛
水题 1(5分)2(7分)12分
复数 3(13分)13分
排序 5(9分)6(11分)20分
找规律 7(19分)19分
尺取法 8(21分)21分
DFS/爆破 9(23分)23分
DP 4(17分)10(25分)42分
搜索基本理论
1、回溯法:当把问题分成若干个步骤递归求解时,如果当前步骤没有合法选择,则函数将返回上一级递归调用,这种现象就称回溯。
2、路径寻找问题:路径寻找问题可以归结为隐式图的遍历,它的任务是找到一条从初始状态到终止状态的最优路径,而不是像回溯法那样找到一个符合某些要求的解。常见的两种方法是:深度优先搜索,广度优先搜索。
枚举
在数学和计算机科学理论中,一个集的枚举是列出某些有穷序列集的所有成员的程序,或者是一种特定类型对象的计数。这两种类型经常(但不总是)重叠。枚举在日常生活中很常见,例如表示星期的SUNDAY、MONDAY、TUESDAY、WEDNESDAY、THURSDAY、FRIDAY、SATURDAY就是一个枚举。

凑算式

三羊献瑞问题

蓝桥杯_搜索算法(DFS和BFS)_第1张图片先分析一下。
// 祥瑞生辉
// +三羊献瑞
//=三羊生瑞气
//0祥1瑞2生3辉4三5羊6献7气
// 0123
// +4561
//=45217
我用的代码是这样的

#include
int index;
int a[8]={};
int visited[10]={0};
//   祥瑞生辉
//  +三羊献瑞
//=三羊生瑞气
//0祥1瑞2生3辉4三5羊6献7气
//  0123
// +4561
//=45217
void dfs(int index){
	if(index==8){
		int sum=(a[0]*1000+a[1]*100+a[2]*10+a[3])+(a[4]*1000+a[5]*100+a[6]*10+a[1]);
		if(sum==a[4]*10000+a[5]*1000+a[2]*100+a[1]*10+a[7])
			printf("%d %d %d %d\n",a[4],a[5],a[6],a[1]);
	}else{
		for(int i=0;i<=9;i++)
		{
			if(index == 0 && i == 0) {// 祥字不可能为0
					continue;//i++,直接执行下一次循环
				}
			if(index == 4 && i != 1) {// 三字必为1
					continue; 
				}
			if(visited[i] == 0) {// 没被访问过,可访问
					visited[i] = 1; // 访问 i, 做标记
					a[index] = i;//给数组里放数字
					dfs(index+1);//枚举下一种情况
					visited[i] = 0;  //回溯, 清除标记
				}
		}
	}
}

int main(){
dfs(0);
}

写的过程中出现了一个问题,变量的作用域问题。
所谓作用域(Scope),就是变量的有效范围,就是变量可以在哪个范围之内使用。有些变量可以在所有代码文件中使用,有些变量只能在当前的文件中使用,有些变量只能在函数内部使用,有些变量只能在 for 循环内部使用。
变量的作用域由变量的定义位置决定,在不同位置定义的变量,它的作用域是不一样的。一种是只能在函数内部使用的变量,另一种是可以在所有代码文件中使用的变量。

一、在函数内部定义的变量(局部变量)

在函数内部定义的变量,它的作用域也仅限于函数内部,出了函数就不能使用,我们将这样的变量称为局部变量(Local Variable)。函数的形参也是局部变量,也只能在函数内部使用。
例如:

#include 
int sum(int m, int n){
    int i, sum=0;
    //m、n、i、sum 都是局部变量,只能在 sum() 内部使用
    for(i=m; i<=n; i++){
        sum+=i;
    }
    return sum;
}
int main(){
    int begin = 5, end = 86;
    int result = sum(begin, end);
    //begin、end、result 也都是局部变量,只能在 main() 内部使用
    printf("The sum from %d to %d is %d\n", begin, end, result);
    return 0;
}

其中,m、n、i、sum 是局部变量,只能在 sum() 内部使用;begin、end、result 也是局部变量,只能在 main() 内部使用。
对局部变量:
1.main() 也是一个函数,在 main() 内部定义的变量也是局部变量,只能在 main() 函数内部使用。
2.形参也是局部变量,将实参传递给形参的过程,就是用实参给局部变量赋值的过程,它和a=b; sum=m+n;这样的赋值没有什么区别。

二、在所有函数外部定义的变量(全局变量)

C语言允许在所有函数的外部定义变量,这样的变量称为全局变量(Global Variable)。
全局变量的默认作用域是整个程序,也就是所有的代码文件,包括源文件(.c文件)和头文件(.h文件)。如果给全局变量加上 static 关键字,它的作用域就变成了当前文件,在其它文件中就无效。目前编写的代码都是在一个源文件中,所以暂时不考虑 static 关键字。
例如:
定义一个函数,根据长方体的长宽高求它的体积以及三个面的面积。

#include 
//定义三个全局变量,分别表示三个面的面积
int s1 = 0, s2 = 0, s3 = 0;
int vs(int length, int width, int height){
    int v;  //体积
    v = length * width * height;
    s1 = length * width;
    s2 = width * height;
    s3 = length * height;
    return v;
}
int main(){
    int v = 0;
    v = vs(15, 20, 30);
    printf("v=%d, s1=%d, s2=%d, s3=%d\n", v, s1, s2, s3);
    v = vs(5, 17, 8);
    printf("v=%d, s1=%d, s2=%d, s3=%d\n", v, s1, s2, s3);
    return 0;
}

运行结果:
v=9000, s1=300, s2=600, s3=450
v=680, s1=85, s2=136, s3=40
(1)根据题意,借助一个函数得到四份数据:体积 v 以及三个面的面积 s1、s2、s3。C语言中的函数只能有一个返回值,只能将其中的一份数据(也就是体积 v)放到返回值中,其它三份数据(也就是面积 s1、s2、s3)只能保存到全局变量中。
(2)C语言代码从前往后依次执行,变量在使用之前必须定义或者声明,全局变量 s1、s2、s3 定义在程序开头,所以在 vs() 和 main() 中都有效。
(3)在 vs() 中将求得的面积放到 s1、s2、s3 中,在 main() 中能够顺利取得它们的值,这说明:在一个函数内部修改全局变量的值会影响其它函数,全局变量的值在函数内部被修改后并不会自动恢复,它会一直保留该值,直到下次被修改。
(4)全局变量也是变量,变量只能保存一份数据,一旦数据被修改了,原来的数据就被冲刷掉,再也无法恢复,所以不管是全局变量还是局部变量,一旦它的值被修改,这种影响都会一直持续下去,直到再次被修改。

学霸的迷宫
(bfs问题)
问题描述
  学霸抢走了大家的作业,班长为了帮同学们找回作业,决定去找学霸决斗。但学霸为了不要别人打扰,住在一个城堡里,城堡外面是一个二维的格子迷宫,要进城堡必须得先通过迷宫。因为班长还有妹子要陪,磨刀不误砍柴功,他为了节约时间,从线人那里搞到了迷宫的地图,准备提前计算最短的路线。可是他现在正向妹子解释这件事情,于是就委托你帮他找一条最短的路线。
输入格式
  第一行两个整数n, m,为迷宫的长宽。
  接下来n行,每行m个数,数之间没有间隔,为0或1中的一个。0表示这个格子可以通过,1表示不可以。假设你现在已经在迷宫坐标(1,1)的地方,即左上角,迷宫的出口在(n,m)。每次移动时只能向上下左右4个方向移动到另外一个可以通过的格子里,每次移动算一步。数据保证(1,1),(n,m)可以通过。
输出格式
  第一行一个数为需要的最少步数K。
  第二行K个字符,每个字符∈{U,D,L,R},分别表示上下左右。如果有多条长度相同的最短路径,选择在此表示方法下字典序最小的一个。
样例输入
Input Sample 1:
3 3
001
100
110
Input Sample 2:
3 3
000
000
000
样例输出
Output Sample 1:
4
RDRD
Output Sample 2:
4
DDRR
数据规模和约定
有20%的数据满足:1<=n,m<=10
有50%的数据满足:1<=n,m<=50
有100%的数据满足:1<=n,m<=500。
代码
看过也看不懂。。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

const int maxn = 600 + 10;
const int INF = 100000000;
typedef pair<int,int> P;
// D(下), L(左), R(右), U(上) 
int dir[4][2] = { {1, 0}, {0, -1}, {0, 1}, {-1, 0}};
char dir_c[4] = {'D', 'L', 'R', 'U'};
int row, col;               //行列 
char maze[maxn][maxn];      //表示迷宫的字符串的数组 
int d[maxn][maxn];          //到各个位置的最短距离的数组 
string Min;                 //U,D,L,R
queue<P> que;             
void input();        
bool judge(int r, int c);
int BFS();

void input()
{
    scanf("%d%d", &row, &col);
    for (int i = 0; i < row; i++) {
        for (int j = 0; j < col; j++) {
            cin >> maze[i][j];
        }
    }
    //所有位置初始化
    for (int i = 0; i < row; i++) {
        for (int j = 0; j < col; j++) {
            d[i][j] = INF;
        }
    } 
}

bool judge(int r, int c)
{
    return (r >= 0 && r < row) && (c >= 0 && c < col)
            && (maze[r][c] != '1');  //可走 
}

int BFS()
{
    //将起点假如队列, 并把这一地点的距离设置为 0
    que.push(P(0, 0));
    queue<string> path;
    path.push("");
    
    d[0][0] = 0;
    Min = "";
    
    while (!que.empty())
    {
        P p = que.front(); que.pop();
        string t = path.front(); path.pop();
        
        if (p.first == row - 1 && p.second == col - 1) {
            Min = t;             //因为我的方向就是按照字典序 DLRU,所以这时候形成最短路线的路径就是按照字典序最小的路线! 
            break;
        }
        
        for (int i = 0; i < 4; i++) {
            //移动之后的位置为(nx,ny)
            int nx = p.first + dir[i][0], ny = p.second + dir[i][1];
            
            //可以走,且尚未访问(d[nv][ny]==INF
            if (judge(nx, ny) && d[nx][ny] == INF) {
                //加入到队列,并且到该位置的距离确定为到p的距离+1
                que.push(P(nx, ny));
                path.push(t + dir_c[i]);                 //这里数据结构组织的并不好,我应该一开始就把路径和位置组合成结构体,会更方便 
                d[nx][ny] = d[p.first][p.second] + 1;    //因为是的方向就是按照 DLRU字典序遍历,所以不需要有什么额外的判断,只需要和行走路线 
                maze[nx][ny] = '1';                      //一起出队,入队就可以了! 
            } 
        }
    }
    return d[row - 1][col - 1];
}

void solve()
{
    input();
    int res = BFS();
    printf("%d\n%s\n", res, Min.c_str());
}

int main()
{
    solve();
    return 0;
}

你可能感兴趣的:(蓝桥杯,c语言,c++)