蓝桥杯中的题目合集
算法学习
深度优先搜索
搜索迷宫
探索迷宫,看是否能够找到一条通路,S
为起始点,T
为终点,通路为.
,障碍为*
第一行输入m n
, 分别表示迷宫的行数和列数
下面一次输入迷宫图形
5 6
....S*
.***..
.*..*.
*.***.
.T....
问题思路
找到通路DFS算法:
1、如果当前已经到达终点,return结果为true
否则
2、标记当前结点已经走过,当前结点记号变换
从当前结点进行四个方向查找,如果下一个位置合法(在图内,能够到达,没有访问过),则继续DFS
如果在当前结点出发成功到达终点,返回true
3、当前结点四个方向不能到达则
恢复访问标记为未访问,恢复初始符号
找到最短通路算法:
(设定已经找到的步数为一个很大的值)
1、如果当前步数大于已经找到的步数,return
2、如果当前位置为终点,并且步数小与已经找到通路的步数,记录步数,return
3、标记当前结点已经访问
从当前结点开始,向四个方向继续寻找
恢复访问标记为未访问
#include <iostream>
#include <cstdlib>
#include <cstring>
using namespace std;
#define MAXN 110
char maze[MAXN][MAXN];
int vis[MAXN][MAXN];
int ans = 1e9;
int m, n;
int dir[4][2] = {
{
-1, 0}, {
0, 1}, {
1, 0}, {
0, -1} };
bool in(int x,int y) {
return x >= 0 && x < m && y >= 0 && y < n && maze[x][y] != '*';
}
void dfs(int x, int y, int step) {
if(step > ans) return;
if(maze[x][y] == 'T') {
ans = step;
return;
}
vis[x][y] = 1;
for(int i = 0; i < 4; i++){
int tx = x + dir[i][0];
int ty = y + dir[i][1];
if(in(tx, ty) && !vis[tx][ty] )
dfs(tx, ty, step + 1);
}
//not find
vis[x][y] = 0;
}
int main() {
memset(vis, 0, sizeof(vis));
int i, j, sx = 0, sy = 0;
cin >> m >> n;
for(i = 0; i < m; i++) {
cin >> maze[i];
}
for(sx = 0, sy = 0, i = 0; i < m; i++){
for(j = 0; j < n; j++) {
if(maze[i][j] == 'S'){
sx = i, sy = j;
j = n, i = m;
}
}
}
dfs(sx, sy, 0);
cout << ans << endl;
system("pause");
return 0;
}
详细算法
bool dfs(int x, int y) {
if(maze[x][y] == 'T') return true;
vis[x][y] = 1;
maze[x][y] = 'm';
//up
int tx = x - 1, ty = y;
if(in(tx, ty) && !vis[tx][ty] && dfs(tx, ty)) return true;
//right
tx = x, ty = y + 1;
if(in(tx, ty) && !vis[tx][ty] && dfs(tx, ty)) return true;
//down
tx = x + 1, ty = y;
if(in(tx, ty) && !vis[tx][ty] && dfs(tx, ty)) return true;
//left
tx = x, ty = y -1;
if(in(tx, ty) && !vis[tx][ty] && dfs(tx, ty)) return true;
//not find
vis[x][y] = 0;
maze[x][y] = '.';
return false;
}
简便算法
bool dfs(int x, int y) {
if(maze[x][y] == 'T') return true;
vis[x][y] = 1;
maze[x][y] = 'm';
for(int i = 0; i < 4; i++){
int tx = x + dir[i][0];
int ty = y + dir[i][1];
if(in(tx, ty) && !vis[tx][ty] && dfs(tx, ty)) return true;
}
//not find
vis[x][y] = 0;
maze[x][y] = '.';
return false;
}
计算走的步数
void dfs(int x, int y, int step) {
// 添加剪枝约束
if(step > ans) return;
if(maze[x][y] == 'T') {
ans = step;
return;
}
vis[x][y] = 1;
for(int i = 0; i < 4; i++){
int tx = x + dir[i][0];
int ty = y + dir[i][1];
if(in(tx, ty) && !vis[tx][ty] )
dfs(tx, ty, step + 1);
}
//not find
vis[x][y] = 0;
}
最大连接块
问题描述
在m * n
的图中,用#
表示可以走到,用.
表示不可以走到
在图中寻找最大的#连通块
5 6
.#....
..#...
..#..#
...##.
.#....
算法思路:
1、在图中进行遍历,找到当前符号为#,并且没有访问的,初始化计数变量,进行遍历。
如果最终结果大于已知计数个数,则更新结果
2、遍历算法
如果当前结点不合法(不在图中、结点不是#、已经被访问),return结束
标记当前结点已经访问
增加连通个数
从上下左右四个方向进行遍历
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
#define MAXN 110
char maze[MAXN][MAXN];
bool vis[MAXN][MAXN];
int m, n, ans = 0, cnt, sx, sy;
bool in(int x, int y) {
return x >= 0 && x < m && y >= 0 && y < n;
}
void dfs(int x, int y) {
if(!in(x, y) || maze[x][y] != '#' || vis[x][y]) return;
vis[x][y] = 1;
cnt++;
dfs(x, y + 1);
dfs(x + 1, y);
dfs(x, y - 1);
dfs(x - 1, y);
}
int main()
{
cin >> m >> n;
for(int i = 0; i < m; i++) {
cin >> maze[i];
}
for(int i = 0; i < m; i++) {
for(int j = 0; j < n; j++) {
if(!vis[i][j] && maze[i][j] == '#') {
cnt = 0;
dfs(i, j);
if(cnt > ans) ans = cnt;
}
}
}
cout << ans << endl;
system("pause");
return 0;
}
深度优先算法的剪枝策略
普通剪枝
计算和为定值的搭配个数
计算从1,2,3,…,30这些数中选出8个数并且和为200的组合个数
#include <cstdlib>
#include <iostream>
#include <cstring>
using namespace std;
#define MAXN 30
int sum = 200;
int arr[40];
int n = 30;
int k = 8;
int ans = 0;
int sel[40];
void dfs(int s, int cnt, int pos) {
if(s > sum || cnt > k) return;
if(s == sum && cnt == k) {
ans++;
return;
}
for(int i = pos; i < n; i++) {
if(!sel[i]) {
sel[i] = 1;
dfs(s + arr[i], cnt + 1, i + 1);
sel[i] = 0;
}
}
}
int main()
{
for(int i = 0; i < n; i++) {
arr[i] = i + 1;
}
memset(sel, 0, sizeof(sel));
dfs(0, 0, 0);
cout << ans << endl;
system("pause");
return 0;
}
普通算法:时间复杂度为O(n2)
void dfs(int s, int cnt, int pos) {
if(s == sum && cnt == k) {
ans++;
return;
}
for(int i = pos; i < n; i++) {
if(!sel[i]) {
sel[i] = 1;
dfs(s + arr[i], cnt + 1, i + 1);
sel[i] = 0;
}
}
}
剪枝策略:时间复杂度为O(n)
void dfs(int s, int cnt, int pos) {
if(s > sum || cnt > k) return; //剪枝语句
if(s == sum && cnt == k) {
ans++;
return;
}
for(int i = pos; i < n; i++) {
if(!sel[i]) {
sel[i] = 1;
dfs(s + arr[i], cnt + 1, i + 1);
sel[i] = 0;
}
}
}
奇偶剪枝
在一个
n*m
图中将该位置的横坐标和纵坐标求和,如果是偶数就标记白色,如果是偶数就标记为黑色
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7Loa6MW8-1605282562792)(E:\images\markdown\white_black_map.png)]
上图中相邻两个格子的颜色不同,表示每走一步颜色就会不一样。更为普遍的结论是:走奇数步会改变颜色,走偶数步颜色不变。
题目描述
在一个n×m
大小的迷宫中,其中字符S
表示起点,字符D
表示出口,字符X
表示墙壁,字符.
表示空地,你需要从S
出发走到D
,每次只能向上下左右相邻的位置移动,并且不能走出地图,不能走进墙壁。
每次移动小号1时间,走过的路都会塌陷,因此不能回头或着原地不动。现在已知出口的大门会在T时间内打开,判断在0时间从起点是否能够逃离迷宫
例子
4 4 5
S.X.
..X.
..XD
....
#include <cstdlib>
#include <iostream>
using namespace std;
const int MAXN = 11;
char maze[MAXN][MAXN];
int vis[MAXN][MAXN];
bool OK;
int T, n, m, sx, sy, ex, ey;
int dir[4][2] = {
{
-1, 0}, {
0, 1}, {
1, 0}, {
0, -1}};
void dfs(int x, int y, int t) {
if(OK) return;
if(t > T) return; //overtime
if(t == T && maze[x][y] == 'D') {
//can be find
OK = true;
return;
}
vis[x][y] = true;
maze[x][y] = '#';
for(int i = 0; i < 4; i++) {
int tx, ty;
tx = x + dir[i][0], ty = y + dir[i][1];
if(tx < 0 || tx >= n || ty < 0 || ty >= m || maze[tx][ty] == 'X' || vis[tx][ty])
continue; //notice the use of "continue" here
dfs(tx, ty, t + 1);
}
vis[x][y] = false;
maze[x][y] = '.';
}
int main()
{
cin >> n >> m >> T;
for(int i = 0; i < n; i++)
cin >> maze[i];
for(int i = 0; i < n; i++) {
for(int j = 0; j <