今日学习了广度优先遍历,并明确DP包含于最短路问题,DP问题其实是没有环的最短路问题,当所有边的权重都为1时,最短路问题可以用BFS求解,图的存储
BFS模板
#include <queue>//包含头文件,不用自己实现队列
queue<int> q;
q.push(x);//把搜索的起点加入
while(!q.empty()){
int t=q.front();
q.pop();
q.push(xxx);//把t邻接的点加入
}
图的存储
图的存储方式一般有邻接矩阵法(存稠密图)和邻接表法
邻接表相当于每一个点都开一个单链表存邻接点和权重
//图的邻接表的数组模拟模板
int h[N],e[2*N],ne[2*N],idx;//邻接表的数组存储
//h[N]代表每个结点的表头,以每个结点为表头存他的邻接点
//e存的是点,如果a邻接b,说明a的邻接表中接的e应该存b
//idx作为索引,来看用到了第几个位置
//ne作为指针的模拟,e对应的ne里的值指向他下一个是存在哪个下标
void add(int a,int b){
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}//因为是树,为无向图,所以加两条边
//遍历
x为1-N任意一个结点
for(int i=h[x];i!=-1;i=ne[i])
//则e[i]中存的就是对应的结点
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
思路:本题只需要利用BFS特性,从起点进行扩散,即可,先扩散到终点的一定是最短的距离
因此,用一个dis数组记录第一次扩散的距离即可,每一次到达的点的dis值为来的点+1,则终点的dis值刚好为最短距离
//参考yxc大佬的思路
#include <iostream>
#include <cstring>
#include <utility>
#include <queue>
#define PII pair<int,int>
using namespace std;
int n,m;
const int N=105;
int map[N][N],dis[N][N];//存地图,存距离
int dx[4]={0,0,1,-1},dy[4]={-1,1,0,0};//四个方向向量
int bfs(){
queue<PII> q;
q.push({0,0});//把起点加入队列
dis[0][0]=0;
while(!q.empty()){
PII tem=q.front();
q.pop();
for(int i=0;i<4;i++){
int x=tem.first+dx[i],y=tem.second+dy[i];//更新后的向量
if(x>=0&&x<n&&y>=0&&y<m&&map[x][y]==0&&dis[x][y]==-1){//不出边界,能走,且没走过
dis[x][y]=dis[tem.first][tem.second]+1;
q.push({x,y});
}
}
}
return dis[n-1][m-1];
}
int main(){
cin>>n>>m;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
cin>>map[i][j];
memset(dis,-1,sizeof dis);//最短路初始化为-1
cout<<bfs();
}
Acwing.804 树的重心
给定一颗树,树中包含n个结点(编号1~n)和n-1条无向边。
请你找到树的重心,并输出将重心删除后,剩余各个连通块中点数的最大值。
重心定义:重心是指树中的一个结点,如果将这个点删除后,剩余各个连通块中点数的最大值最小,那么这个节点被称为树的重心。
输入格式
第一行包含整数n,表示树的结点数。
接下来n-1行,每行包含两个整数a和b,表示点a和点b之前存在一条边。
输出格式
输出一个整数m,表示重心的所有的子树中最大的子树的结点数目。
数据范围
1≤n≤105
输入样例
9
1 2
1 7
1 4
2 8
2 5
4 3
3 9
4 6
输出样例:
4
思路:用DFS往下搜索时,向上返回子树结点的总数,同时记录该结点分支中结点数最大的那个,判断n-分支结点总数-1和最大的那个哪个大,大的就是删除该点后,连通集中最大结点数,枚举删除所有点的情况,然后取最小值即可
//代码参考yxc,之后根据题意理解后默写
#include <iostream>
#include <cstring>
using namespace std;
const int N=106;
int n,ans=N-1;//结果的最大值就是N-1,去掉一个叶子结点
int h[N],e[2*N],ne[2*N],idx;//邻接表的数组存储
bool visit[N];
void add(int a,int b){
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
int dfs(int u){
visit[u]=true;//记录访问
int num=1,msize=0;//num用来记录包括自己结点的子树所有结点
//msize来记录子树,所有结点中的最大值,不包括自己
for(int i=h[u];i!=-1;i=ne[i]){//访问他的邻接点
int j=e[i];//找到里面存的点
if(visit[j]) continue;//如果访问过就跳过
int s=dfs(j);//求子树的结点个数
msize=max(msize,s);//回溯回来求最大的那个
num+=s;//求子树所有结点
}
msize=max(n-num,msize);//求出去掉该节点剩下联通集中结点最大值
ans=min(msize,ans);
return num;//返回该子树结点
}
int main(){
cin>>n;
memset(h,-1,sizeof(h));
for(int i=0;i<n-1;i++){
int x,y;
cin>>x>>y;
add(x,y),add(y,x);
}
dfs(n-2);//随便1-n访问一个结点就行,因为相互联通
cout<<ans;
return 0;
}
今日浅浅复习BFS和DFS遍历图