存储结构:
首先要知道存储结构如何用数组模拟出来:
树是特殊的图,无向图可以看作有向图,所以都可以看作是有向图进行存储。
树与图的存储:
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;
}
这些题都是通过距离数组储存来对层次进行计数和判断是否遍历过!!!