ACwing算法备战蓝桥杯——Day12——BFS

存储结构:

首先要知道存储结构如何用数组模拟出来:

树是特殊的图,无向图可以看作有向图,所以都可以看作是有向图进行存储。
树与图的存储:
1.二维数组(邻接表),存储稠密图
2.邻接表(最常用)
模拟邻接表:
// 对于每个点k,开一个单链表,存储k所有可以走到的点。h[k]存储这个单链表的头结点
int h[N], e[N], ne[N], idx;

// 添加一条边a->b
void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

// 初始化
idx = 0;
memset(h, -1, sizeof h);

模板:

就是树的层序遍历,利用队列维护

暴力枚举每一个状态,时间复杂度高

queue<int> q;
st[1] = true; // 表示1号点已经被遍历过
q.push(1);

while (q.size())
{
    int t = q.front();
    q.pop();

    for (int i = h[t]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (!st[j])
        {
            st[j] = true; // 表示点j已经被遍历过
            q.push(j);
        }
    }
}

经典例题:

走迷宫:

给定一个 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 <queue>
#define x first
#define y second
using namespace std;
typedef pair<int,int> PII;
const int N=110;
int n,m;
int g[N][N],d[N][N];//g数组为迷宫,d为当前点到原点的距离,用d数组实现对层次的计数
int bfs(){
    //初始化
    queue<PII> Q;
    memset(d,-1,sizeof(d));
    PII temp={0,0};
    Q.push(temp);
    d[0][0]=0;
    int dx[4]={0,0,-1,1},dy[4]={1,-1,0,0};
    //循环
    while(Q.size()){
        PII a=Q.front();
        Q.pop();
        for(int i=0;i<4;i++){
            int x1=a.x+dx[i];
            int y1=a.y+dy[i];
            if(x1<n&&x1>=0&&y1<m&&y1>=0&&!g[x1][y1]&&d[x1][y1]==-1){
                PII b={x1,y1};
                Q.push(b);
                d[x1][y1]=d[a.x][a.y]+1;
            }
        }
    }
    return d[n-1][m-1];
}
int main(){
    cin>>n>>m;
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            scanf("%d",&g[i][j]);
        }
    }
    cout<<bfs()<<endl;
    return 0;
}

八数码:

在一个 3×3 的网格中,1∼8 这 8 个数字和一个 x 恰好不重不漏地分布在这 3×3 的网格中。

例如:

1 2 3
x 4 6
7 5 8
在游戏过程中,可以把 x 与其上、下、左、右四个方向之一的数字交换(如果存在)。

我们的目的是通过交换,使得网格变为如下排列(称为正确排列):

1 2 3
4 5 6
7 8 x
例如,示例中图形就可以通过让 x 先后与右、下、右三个方向的数字交换成功得到正确排列。

交换过程如下:

1 2 3   1 2 3   1 2 3   1 2 3
x 4 6   4 x 6   4 5 6   4 5 6
7 5 8   7 5 8   7 x 8   7 8 x
现在,给你一个初始网格,请你求出得到正确排列至少需要进行多少次交换。

输入格式
输入占一行,将 3×3 的初始网格描绘出来。

例如,如果初始网格如下所示:

1 2 3 
x 4 6 
7 5 8 
则输入为:1 2 3 x 4 6 7 5 8

输出格式
输出占一行,包含一个整数,表示最少交换次数。

如果不存在解决方案,则输出 −1。

输入样例:
2 3 4 1 5 x 7 6 8
输出样例
19
代码:

#include <iostream>
#include <queue>
#include <string>
#include <algorithm>
#include <unordered_map>
using namespace std;
queue<string> Q;//通过存储每个交换后的整个数组来表示遍历
unordered_map<string,int> d;
int bfs(string star){
    //初始化
    string end="12345678x";
    Q.push(star);
    d[star]=0;
    int dx[4]={0,0,-1,1},dy[4]={1,-1,0,0};
    //循环
    while(Q.size()){
        string temp=Q.front();//取队头
        Q.pop();
        int distance=d[temp];
        if(temp==end) return d[end];//如果等于目标值,就返回距离
        int x0,y0;//寻找x的位置 
        int l=0;
        for(;l<9;l++){
            if(temp[l]=='x'){
                x0=l/3;
                y0=l%3;
                break;
            }
        }
        for(int i=0;i<4;i++){//枚举上下左右四个方向
            int x1=x0+dx[i];
            int y1=y0+dy[i];
            if(x1<3&&x1>=0&&y1<3&&y1>=0){//枚举每个未知状态,已知状态就不加入队列
                int j=x1*3+y1;//二维转换为一维
                string exc=temp;//复制一个字符串
                swap(exc[l],exc[j]);//交换两个位置的字符
                if(d.count(exc)) continue;//如果交换后的字符串已经出现过,那么就不是我们要找的新状态,就舍去
                Q.push(exc);//入队列
                d[exc]=distance+1;//交换次数加一
            }
        }
        
    }
    return -1;//当所有状态都更新完毕,也没找到就返回-1
}
int main(){
    string star;
    for(int i=0;i<9;i++){
        string temp;
        cin>>temp;
        star+=temp;
    }
    cout<<bfs(star)<<endl;
    return 0;
}

微博转发:

微博被称为中文版的 Twitter。

微博上的用户既可能有很多关注者,也可能关注很多其他用户。

因此,形成了一种基于这些关注关系的社交网络。

当用户在微博上发布帖子时,他/她的所有关注者都可以查看并转发他/她的帖子,然后这些人的关注者可以对内容再次转发…

现在给定一个社交网络,假设只考虑 L 层关注者,请你计算某些用户的帖子的最大可能转发量。

补充
如果 B 是 A 的关注者,C 是 B 的关注者,那么 A 的第一层关注者是 B,第二层关注者是 C。

输入格式
第一行包含两个整数,N 表示用户数量,L 表示需要考虑的关注者的层数。

假设,所有的用户的编号为 1∼N。

接下来 N 行,每行包含一个用户的关注信息,格式如下:

M[i] user_list[i]
M[i] 是第 i 名用户关注的总人数,user_list[i] 是第 i 名用户关注的 M[i] 个用户的编号列表。

最后一行首先包含一个整数 K,表示询问次数,然后包含 K 个用户编号,表示询问这些人的帖子的最大可能转发量。

输出格式
按顺序,每行输出一个被询问人的帖子最大可能转发量。

假设每名用户初次看到帖子时,都会转发帖子,只考虑 L 层关注者。

数据范围
1≤N≤1000,
1≤L≤6,
1≤M[i]≤100,
1≤K≤N
输入样例:
7 3
3 2 3 4
0
2 5 6
2 3 1
2 3 4
1 4
1 5
2 2 6
输出样例:
4
5


代码加题解:
//图的广度优先搜索
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int N=1010,M=100010;
int h[N],e[M],ne[M],idex;//邻接表,h数组存的是每个点,e数组存边,ne数组存相邻点间的关系
void add(int a,int b){//加边
        ne[idex]=h[a],e[idex]=b,h[a]=idex++;
}
int bfs(int a,int L){
    //初始化
    queue<int> Q;//加入结点
    int d[N];//看是否遍历过,并判断层数
    memset(d,-1,sizeof(d));
    Q.push(a);
    d[a]=0;
    int cnt=-1;//转发次数
    //
    while(Q.size()){
        int t=Q.front();
        if(d[t]>L) break;
        Q.pop();
        cnt++;
        for(int p=h[t];p!=-1;p=ne[p]){//遍历单链表
            if(d[e[p]]!=-1) continue;
            Q.push(e[p]);
            d[e[p]]=d[t]+1;
        }
    }
    return cnt;
}
int main(){
    int n,L;
    cin>>n>>L;
    memset(h,-1,sizeof(h));//-1表示没有以这个点为起点的边
    for(int b=1;b<=n;b++){//储存数据
        int m;
        cin>>m;
        for(int j=1;j<=m;j++){
            int a;
            scanf("%d",&a);
            add(a,b);
        }
    }
    //bfs
    int m;
    cin>>m;//询问次数
    while(m--){
        int q;
        cin>>q;//从哪个点开始进行广度优先遍历,也可看作层序遍历
        cout<<bfs(q,L)<<endl;
    }
    return 0;
}

这些题都是通过距离数组储存来对层次进行计数和判断是否遍历过!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

切勿踌躇不前

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值