蓝桥杯搜索入门

蓝桥杯搜索入门

搜索分为深度优先搜索(DFS)和广度优先搜索(BFS)。通俗来说深度优先搜索就是一条路走到黑,而广度优先搜索则像是从多个方向同时搜索,按照搜索的先后顺序一层层扩展。如果有了解过树的几种遍历方式,就会发现树的前序、中序、后序遍历是深度优先搜索,层次遍历是广度优先搜索。

深度优先搜索(DFS)

AcWing 92. 递归实现指数型枚举

从 1∼n 这 n 个整数中随机选取任意多个,输出所有可能的选择方案。

输入格式
输入一个整数 n。

输出格式
每行输出一种方案。

同一行内的数必须升序排列,相邻两个数用恰好 1 个空格隔开。

对于没有选任何数的方案,输出空行。

本题有自定义校验器(SPJ),各行(不同方案)之间的顺序任意。

数据范围
1≤n≤15
输入样例:
3
输出样例:

3
2
2 3
1
1 3
1 2
1 2 3
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
int n,path[20];
void dfs(int step){
    if(step>=n+1){
        for(int i=1;i<=n;i++){
            if(path[i]) cout<<path[i]<<" ";
        }
        cout<<endl;
        return;
    }
    path[step]=step;
    dfs(step+1);
    path[step]=0;
    dfs(step+1);
}
int main()
{
    cin>>n;
    dfs(1);
    return 0;
}
//第二种写法
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=20;
int n,path[maxn];
void dfs(int step,int u){
    if(step>n) return;
    for(int i=0;i<step;i++) cout<<path[i]<<" ";
    cout<<endl;
    for(int i=u+1;i<=n;i++){
        path[step]=i;
        dfs(step+1,i);
    }
}
int main()
{
    cin>>n;
    dfs(0,0);
    return 0;
}

AcWing 94. 递归实现排列型枚举

把 1∼n 这 n 个整数排成一行后随机打乱顺序,输出所有可能的次序。

输入格式
一个整数 n。

输出格式
按照从小到大的顺序输出所有方案,每行 1 个。

首先,同一行相邻两个数用一个空格隔开。

其次,对于两个不同的行,对应下标的数一一比较,字典序较小的排在前面。

数据范围
1≤n≤9
输入样例:
3
输出样例:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
int n,path[20];
bool vis[20];
void dfs(int step){
    if(step>=n){
        for(int i=0;i<step;i++) cout<<path[i]<<" ";
        cout<<endl;
        return ;
    }
    for(int i=1;i<=n;i++){
        if(vis[i]) continue;
        path[step]=i;
        vis[i]=true;
        dfs(step+1);
        path[step]=0;
        vis[i]=false;
    }
}
int main()
{
    cin>>n;
    dfs(0);
    return 0;
}

AcWing 93. 递归实现组合型枚举

从 1∼n 这 n 个整数中随机选出 m 个,输出所有可能的选择方案。

输入格式
两个整数 n,m ,在同一行用空格隔开。

输出格式
按照从小到大的顺序输出所有方案,每行 1 个。

首先,同一行内的数升序排列,相邻两个数用一个空格隔开。

其次,对于两个不同的行,对应下标的数一一比较,字典序较小的排在前面(例如 1 3 5 7 排在 1 3 6 8 前面)。

数据范围
n>0 ,
0≤m≤n ,
n+(n−m)≤25
输入样例:
5 3
输出样例:
1 2 3 
1 2 4 
1 2 5 
1 3 4 
1 3 5 
1 4 5 
2 3 4 
2 3 5 
2 4 5 
3 4 5 
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
int n,m,path[20];
bool vis[20];
void dfs(int step,int start){
    if(step>=m){
        for(int i=0;i<step;i++) cout<<path[i]<<" ";
        cout<<endl;
        return ;
    }
    for(int i=start;i<=n;i++){
        if(vis[i]) continue;
        path[step]=i;
        vis[i]=true;
        dfs(step+1,i+1);
        path[step]=0;
        vis[i]=false;
    }
}
int main()
{
    
    cin>>n>>m;
    dfs(0,1);
    return 0;
}

AcWing 1209. 带分数(2013蓝桥杯省赛)

100 可以表示为带分数的形式:100=3+69258/714
还可以表示为:100=82+3546/197
注意特征:带分数中,数字 1∼9 分别出现且只出现一次(不包含 0)。

类似这样的带分数,100 有 11 种表示法。

输入格式
一个正整数。

输出格式
输出输入数字用数码 1∼9 不重复不遗漏地组成带分数表示的全部种数。

数据范围
1≤N<106
输入样例1:
100
输出样例1:
11
输入样例2:
105
输出样例2:
6
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
int n,path[20],ans;
bool vis[20];
int get(int l,int r){
    int res=0;
    for(int i=l-1;i<=r-1;i++) res=res*10+path[i];
    return res;
}
void dfs(int step){
    if(step>=9){
        for(int i=1;i<=7;i++){
            for(int j=i+1;j<=8;j++){
                int a=get(1,i);
                int b=get(i+1,j);
                int c=get(j+1,9);
                if(b%c==0&&a+b/c==n) ans++;
            }
        }
        return ;
    }
    for(int i=1;i<=9;i++){
        if(vis[i]) continue;
        path[step]=i;
        vis[i]=true;
        dfs(step+1);
        path[step]=0;
        vis[i]=false;
    }
}
int main()
{
    cin>>n;
    dfs(0);
    cout<<ans<<endl;
    return 0;
}

AcWing 1113. 红与黑

有一间长方形的房子,地上铺了红色、黑色两种颜色的正方形瓷砖。

你站在其中一块黑色的瓷砖上,只能向相邻(上下左右四个方向)的黑色瓷砖移动。

请写一个程序,计算你总共能够到达多少块黑色的瓷砖。

输入格式
输入包括多个数据集合。

每个数据集合的第一行是两个整数 W 和 H,分别表示 x 方向和 y 方向瓷砖的数量。

在接下来的 H 行中,每行包括 W 个字符。每个字符表示一块瓷砖的颜色,规则如下

1)‘.’:黑色的瓷砖;
2)‘#’:红色的瓷砖;
3)‘@’:黑色的瓷砖,并且你站在这块瓷砖上。该字符在每个数据集合中唯一出现一次。

当在一行中读入的是两个零时,表示输入结束。

输出格式
对每个数据集合,分别输出一行,显示你从初始位置出发能到达的瓷砖数(记数时包括初始位置的瓷砖)。

数据范围
1≤W,H≤20
输入样例:
6 9 
....#. 
.....# 
...... 
...... 
...... 
...... 
...... 
#@...# 
.#..#. 
0 0
输出样例:
45
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
const int N=30;
char g[N][N];
int n,m,ans,nxt[4][2]={{-1,0},{0,1},{1,0},{0,-1}};
bool vis[N][N];
void dfs(int x,int y){
    ans++;
    vis[x][y]=1;
    for(int i=0;i<4;i++){
        int tx=x+nxt[i][0];
        int ty=y+nxt[i][1];
        if(tx<0||tx>=n||ty<0||ty>=m||vis[tx][ty]||g[tx][ty]=='#') continue;
        dfs(tx,ty);
    }
}
int main()
{
    while(cin>>m>>n){
        if(m+n==0) break;
        for(int i=0;i<n;i++) scanf("%s",g[i]);
        ans=0;
        memset(vis,0,sizeof vis);
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                if(g[i][j]=='@'){
                    dfs(i,j);
                }
            }
        }
        cout<<ans<<endl;
    }

    return 0;
}

AcWing 843. n-皇后问题

n−皇后问题是指将 n 个皇后放在 n×n 的国际象棋棋盘上,使得皇后不能相互攻击到,即任意两个皇后都不能处于同一行、同一列或同一斜线上。

现在给定整数 n,请你输出所有的满足条件的棋子摆法。

输入格式
共一行,包含整数 n。

输出格式
每个解决方案占 n 行,每行输出一个长度为 n 的字符串,用来表示完整的棋盘状态。

其中 . 表示某一个位置的方格状态为空,Q 表示某一个位置的方格上摆着皇后。

每个方案输出完成后,输出一个空行。

注意:行末不能有多余空格。

输出方案的顺序任意,只要不重复且没有遗漏即可。

数据范围
1≤n≤9
输入样例:
4
输出样例:
.Q..
...Q
Q...
..Q.

..Q.
Q...
...Q
.Q..

广度优先搜索(BFS)

AcWing 829. 模拟队列(BFS前置知识)

实现一个队列,队列初始为空,支持四种操作:

push x – 向队尾插入一个数 x;
pop – 从队头弹出一个数;
empty – 判断队列是否为空;
query – 查询队头元素。
现在要对队列进行 M 个操作,其中的每个操作 3 和操作 4 都要输出相应的结果。

输入格式
第一行包含整数 M,表示操作次数。

接下来 M 行,每行包含一个操作命令,操作命令为 push x,pop,empty,query 中的一种。

输出格式
对于每个 empty 和 query 操作都要输出一个查询结果,每个结果占一行。

其中,empty 操作的查询结果为 YES 或 NO,query 操作的查询结果为一个整数,表示队头元素的值。

数据范围
1≤M≤100000,
1≤x≤109,
所有操作保证合法。

输入样例:
10
push 6
empty
query
pop
empty
push 3
push 4
pop
query
push 6
输出样例:
NO
6
YES
4

AcWing 844. 走迷宫

给定一个 n×m 的二维整数数组,用来表示一个迷宫,数组中只包含 0 或 1,其中 0 表示可以走的路,1 表示不可通过的墙壁。

最初,有一个人位于左上角 (1,1) 处,已知该人每次可以向上、下、左、右任意一个方向移动一个位置。

请问,该人从左上角移动至右下角 (n,m) 处,至少需要移动多少次。

数据保证 (1,1) 处和 (n,m) 处的数字为 0,且一定至少存在一条通路。

输入格式
第一行包含两个整数 n 和 m。

接下来 n 行,每行包含 m 个整数(0 或 1),表示完整的二维数组迷宫。

输出格式
输出一个整数,表示从左上角移动至右下角的最少移动次数。

数据范围
1≤n,m≤100
输入样例:
5 5
0 1 0 0 0
0 1 0 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0
输出样例:
8
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<queue>
using namespace std;
typedef pair<int,int> PII;
const int N=110;
int g[N][N],dist[N][N];
int n,m,nxt[4][2]={{-1,0},{0,1},{1,0},{0,-1}};
bool vis[N][N];
void bfs(){
    queue<PII> que;
    que.push({0,0});
    vis[0][0]=true;
    dist[0][0]=0;
    while(!que.empty()){
        PII t =que.front();
        que.pop();
        int x=t.first,y=t.second;
        if(x==n-1&&y==m-1) break;
        for(int i=0;i<4;i++){
            int tx=x+nxt[i][0];
            int ty=y+nxt[i][1];
            if(tx<0||tx>=n||ty<0||ty>=m||vis[tx][ty]||g[tx][ty]) continue;
            vis[tx][ty]=true;
            que.push({tx,ty});
            dist[tx][ty]=dist[x][y]+1;
        }
    }
}

int main()
{
   cin>>n>>m;
   for(int i=0;i<n;i++){
        for(int j=0;j<m;j++) cin>>g[i][j];
   }
   bfs();
   cout<<dist[n-1][m-1]<<endl;
   return 0;
}

AcWing 1233. 全球变暖(2018蓝桥杯省赛)

你有一张某海域 N×N 像素的照片,”.”表示海洋、”#”表示陆地,如下所示:

.......
.##....
.##....
....##.
..####.
...###.
.......
其中”上下左右”四个方向上连在一起的一片陆地组成一座岛屿,例如上图就有 2 座岛屿。

由于全球变暖导致了海面上升,科学家预测未来几十年,岛屿边缘一个像素的范围会被海水淹没。

具体来说如果一块陆地像素与海洋相邻(上下左右四个相邻像素中有海洋),它就会被淹没。

例如上图中的海域未来会变成如下样子:

.......
.......
.......
.......
....#..
.......
.......
请你计算:依照科学家的预测,照片中有多少岛屿会被完全淹没。

输入格式
第一行包含一个整数N。

以下 N 行 N 列,包含一个由字符”#”和”.”构成的 N×N 字符矩阵,代表一张海域照片,”#”表示陆地,”.”表示海洋。

照片保证第 1 行、第 1 列、第 N 行、第 N 列的像素都是海洋。

输出格式
一个整数表示答案。

数据范围
1≤N≤1000
输入样例1:
7
.......
.##....
.##....
....##.
..####.
...###.
.......
输出样例1:
1
输入样例2:
9
.........
.##.##...
.#####...
.##.##...
.........
.##.#....
.#.###...
.#..#....
.........
输出样例2:
1
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<queue>
using namespace std;
typedef pair<int,int> PII;
const int N=1110;
char g[N][N];
int n,m,nxt[4][2]={{-1,0},{0,1},{1,0},{0,-1}};
bool vis[N][N];
int total,bound;
void bfs(int x,int y){
    queue<PII> que;
    que.push({x,y});
    vis[x][y]=true;
    while(!que.empty()){
        PII t = que.front();
        que.pop();
        x=t.first,y=t.second;
        total++;
        bool is_bound=false;
        for(int i=0;i<4;i++){
            int tx=x+nxt[i][0];
            int ty=y+nxt[i][1];
            if(tx<0||tx>=n||ty<0||ty>=n||vis[tx][ty]) continue;
            if(g[tx][ty]=='.'){
                is_bound=true;
                continue;
            }
            que.push({tx,ty});
            vis[tx][ty]=true;
        }
        if(is_bound) bound++;
    }
}
int main()
{
   cin>>n;
   for(int i=0;i<n;i++) cin>>g[i];
   int ans=0;
   for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            if(g[i][j]=='#'&&!vis[i][j]){
                total=bound=0;
                bfs(i,j);
                if(total==bound){
                    ans++;
                }
            }
        }
   }
   cout<<ans<<endl;
   return 0;
}

迷宫(2019蓝桥杯省赛)

【问题描述】
下图给出了一个迷宫的平面图,其中标记为 1 的为障碍,标记为 0 的为可
以通行的地方。
010000
000100
001001
110000
迷宫的入口为左上角,出口为右下角,在迷宫中,只能从一个位置走到这
个它的上、下、左、右四个方向之一。
对于上面的迷宫,从入口开始,可以按DRRURRDDDR 的顺序通过迷宫,
一共 10 步。其中 D、U、L、R 分别表示向下、向上、向左、向右走。
对于下面这个更复杂的迷宫(30 行 50 列),请找出一种通过迷宫的方式,
其使用的步数最少,在步数最少的前提下,请找出字典序最小的一个作为答案。
请注意在字典序中D<L<R<U。(如果你把以下文字复制到文本文件中,请务
必检查复制的内容是否与文档中的一致。在试题目录下有一个文件 maze.txt,
内容与下面的文本相同)
01010101001011001001010110010110100100001000101010
00001000100000101010010000100000001001100110100101
01111011010010001000001101001011100011000000010000
01000000001010100011010000101000001010101011001011
00011111000000101000010010100010100000101100000000
11001000110101000010101100011010011010101011110111
00011011010101001001001010000001000101001110000000
10100000101000100110101010111110011000010000111010
00111000001010100001100010000001000101001100001001
11000110100001110010001001010101010101010001101000
00010000100100000101001010101110100010101010000101
11100100101001001000010000010101010100100100010100
00000010000000101011001111010001100000101010100011
10101010011100001000011000010110011110110100001000
10101010100001101010100101000010100000111011101001
10000000101100010000101100101101001011100000000100
10101001000000010100100001000100000100011110101001
00101001010101101001010100011010101101110000110101
11001010000100001100000010100101000001000111000010
00001000110000110101101000000100101001001000011101
10100101000101000000001110110010110101101010100001
00101000010000110101010000100010001001000100010101
10100001000110010001000010101001010101011111010010
00000100101000000110010100101001000001000000000010
11010000001001110111001001000011101001011011101000
00000110100010001000100000001000011101000000110011
10101000101000100010001111100010101001010000001000
10000010100101001010110000000100101010001011101000
00111100001000010000000110111000000001000000001011
10000001100111010111010001000110111010101101111000
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一
个字符串,包含四种字母 D、U、L、R,在提交答案时只填写这个字符串,填
写多余的内容将无法得分。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string>
#include<queue>
using namespace std;
typedef pair<int,int> PII;
int n=30,m=50;
int nxt[4][2]={{1,0},{0,-1},{0,1},{-1,0}};
char ch[5]="DLRU",g[55][55];
bool vis[55][55];
string path[55][55];
void bfs()
{
    queue<PII> que;
    que.push({0,0});
    vis[0][0]=true;
    while(!que.empty()){
        PII t=que.front();
        que.pop();
        int x=t.first,y=t.second;
        if(x==n-1&&y==m-1) break;
        for(int i=0;i<4;i++){
            int tx=x+nxt[i][0];
            int ty=y+nxt[i][1];
            if(tx<0||tx>=n||ty<0||ty>=m||vis[tx][ty]||g[tx][ty]=='1') continue;
            que.push({tx,ty});
            vis[tx][ty]=true;
            path[tx][ty]=path[x][y]+ch[i];
        }
    }
}
int main()
{
    for(int i=0;i<n;i++) scanf("%s",g[i]);
    bfs();
    cout<<path[n-1][m-1]<<endl;
    return 0;
}

答案:DDDDRRURRRRRRDRRRRDDDLDDRDDDDDDDDDDDDRDDRRRURRUURRDDDDRDRRRRRRDRRURRDDDRRRRUURUUUUUUULULLUUUURRRRUULLLUUUULLUUULUURRURRURURRRDDRRRRRDDRRDDLLLDDRRDDRDDLDDDLLDDLLLDLDDDLDDRRRRRRRRRDDDDDDRR

posted @ 2021-04-16 13:49  陌默丶  阅读(430)  评论(0)    收藏  举报