dfs模板:
dfs可以理解成通过递归运算,计算机每次从一个分支走到底,然后通过回溯找下一个可行的路径
void dfs(int num,......)//参数用来表示状态
{
if(判断边界)
{
...//根据题意添加
return;
}
if(越界或者是不合法状态)
return;
for(扩展方式) //也可能是if根据题意使用适合的方式
{
if(扩展方式所达到状态合法)
{
修改操作;//根据题意来添加
标记;
dfs();
(还原标记);
//是否还原标记根据题意
//如果加上(还原标记)就是 回溯法
}
}
}
bfs模板:
bfs搜索的流程可以理解成从一个点出发,往四周发散,计算机自动每次寻找最近的下一层,所以每一次标记的点都是通过最近的路径标记的点
void bfs()
{
while(q.empty()==false)///队列非空
{
int x=q.front().first,y=q.front().second;//取出队首
q.pop();//队首出队
for (int i=1;i<=n;i++)//n个方向
{
int xx=x+dx[i],yy=y+dy[i];
if (xx>=1 && xx<=n && yy>=1 && yy<=m && vis[xx][yy]==false) //未出界,未被访问过
{
vis[xx][yy]=true;//标记
q.push(make_pair(xx,yy));//入队
ans[xx][yy]=ans[x][y]+1;//步数+1
}
}
}
}
dfs走迷宫(二维数组搜索版)
#include<iostream>
using namespace std;
const int N=5;
int n=5;
bool vis[N][N]={false};//是否已经走过
int tox=2,toy=4;//需要找到的点
int maze[5][5]={
{0,0,0,1,0},
{0,1,0,1,0},
{0,1,1,0,0},
{0,1,1,0,1},
{0,0,0,0,0},
};
int path[N*N][2];
bool able_to_reach=false;
int sx[5]={-1,0,1,0};//上右下左
int sy[5]={0,1,0,-1};
void dfs(int x,int y,int step){
path[step][0]=x;
path[step][1]=y;
if(able_to_reach==true){
return;
}
printf("%d %d\n",x,y);
if(x==tox&&y==toy){
able_to_reach=true;
cout<<"找到了!";
cout<<"路径"<<endl;
for(int i=1;i<=step;i++){
printf("x:%d,y:%d\n",path[i][0],path[i][1]);
}
return;
}
int next_x,next_y;
for(int i=0;i<4;i++){
next_x=x+sx[i];
next_y=y+sy[i];
//next point :next_x,next_y
if(next_x>=0&&next_x<n&&next_y>=0&&next_y<n&&vis[next_x][next_y]==false&&maze[next_x][next_y]==0){
vis[next_x][next_y]=true;
dfs(next_x,next_y,step+1);
vis[next_x][next_y]=false;
}
}
}
int main(){
vis[0][0]=true;
dfs(0,0,1);
return 0;
}
是对着学长视频敲的,最基础的dfs了👆
深度优先搜索(dfs):用递归实现的方法用到了栈,规定一个固定的搜寻方向,将搜索到的存到栈中,若碰到不能实现的地方回溯(即出栈),直到能继续搜索直至搜索结束。
dfsP1605 迷宫
再做一道迷宫题:思路是一样的
#include <iostream>
using namespace std;
int n,m,t,sx,sy,fx,fy;int a[6][6];int sum;
int x1[]={0,0,1,-1};
int y1[]={1,-1,0,0};
void dfs(int x,int y){
if(x==fx&&y==fy){
sum++;
return;
}
for(int i=0;i<4;i++){
if(x+x1[i]>=1&&x+x1[i]<=n&&y+y1[i]>=1&&y+y1[i]<=m&&!a[x+x1[i]][y+y1[i]])
{
a[x+x1[i]][y+y1[i]]=1;
dfs(x+x1[i],y+y1[i]);
a[x+x1[i]][y+y1[i]]=0;
}
}
}
int main()
{
int z,h;
cin>>n>>m>>t>>sx>>sy>>fx>>fy;
for(int i=0;i<t;i++){
cin>>z>>h;
a[z][h]=1;
}
a[sx][sy]=1;
dfs(sx,sy);
cout<<sum;
return 0;
}
dfsP1706 全排列问题(经典例题)
#include <iostream>
using namespace std;
int n;int a[100];
bool b[100]={0};
void dfs(int shu){
for(int i=1;i<=n;i++){
if(b[i]==false){
a[shu]=i;
b[i]=true;
dfs(shu+1);
b[i]=false;
}
}
if(shu==n+1){
for(int i=1;i<n;i++){
cout<<" "<<a[i];
}
cout<<" "<<a[n]<<endl;
return;
}
}
int main()
{
cin>>n;
dfs(1);
return 0;
}
全排列n项可以理解为第一项取1到n的数,然后剩下一个n-1项的全排列,所以使用递归实现
写一个dfs的函数:每一层从1遍历到n,对已经选上的数字标记,一层层选出没被标记的数字。
在满足输出之后通过每一个递归分支把每一个数标记去掉,然后输出这一组数。
& next_permutation方法:
next_permutation是按照字典序找到下一个排列并替代原有序列,下面简单测试它的用法
#include<iostream>
#include<algorithm>
using namespace std;
int main(){
int a[3]={0,1,4};
for(int i=0;i<3;i++){
next_permutation(a,a+3);
for(int j=0;j<3;j++)cout<<a[j]<<" ";
cout<<endl;
}
return 0;
}
这个在algorithm库里面的函数可以积累下来,在这个题目里面可以这么用它:
#include<iostream>
#include<algorithm>
using namespace std;
int main(){
int n;cin>>n;
int a[n];
for(int i=0;i<n;i++)a[i]=i+1;
int sum=1;
for(int i=1;i<=n;i++)sum*=i;
for(int i=0;i<sum;i++){
for(int j=0;j<n;j++)cout<<" "<<a[j];
cout<<endl;
next_permutation(a,a+n);
}
return 0;
}
dfs+bfsP1135 奇怪的电梯
dfs有个点超时了哎:
#include<iostream>
using namespace std;
const int N=1e5;
int n,a,b;//n表示楼层数,a表示出发楼层,b表示结束楼层
int x[N];//记录数量
bool vis[N]={0};//记录是否走过
int sum=N;
void dfs(int aa,int sum_b){//aa表示当前所在位置,sum_b表示当前递归所在分支走过的步数
if(aa==b)sum=min(sum,sum_b);
if(sum<sum_b)return;
vis[aa]=1;
if(aa+x[aa]<=n&&!vis[aa+x[aa]])dfs(aa+x[aa],sum_b+1);
if(aa-x[aa]>=1&&!vis[aa-x[aa]])dfs(aa-x[aa],sum_b+1);
vis[aa]=0;
}
int main(){
cin>>n>>a>>b;
for(int i=1;i<=n;i++)cin>>x[i];
vis[a]=0;
dfs(a,0);
if(sum==N)cout<<"-1";
else cout<<sum;
return 0;
}
学完bfs后改造写的一个:
#include<iostream>
#include<queue>
#include<string.h>
using namespace std;
const int N=1e6;
queue<int>q;
int num[N];//记录数量
int w[N];//记录每层走的层数
bool vis[N]={0};//记录是否走过
int n,a,b;//n表示楼层数,a表示出发楼层,b表示结束楼层
void bfs(){
while(!q.empty())
{
int s=q.front();
q.pop();
int f1=s+w[s];
int f2=s-w[s];
if(f1>=1&&vis[f1]==0)
{
vis[f1]=1;
q.push(f1);
num[f1]=num[s]+1;
}
if(f2>=1&&vis[f2]==0)
{
vis[f2]=1;
q.push(f2);
num[f2]=num[s]+1;
}
}
}
int main(){
cin>>n>>a>>b;
memset(num,-1,sizeof(num));
for(int i=1;i<=n;i++)cin>>w[i];
num[a]=0;vis[a]=1;
q.push(a);
bfs();
if(num[b]==N)cout<<"-1";
else cout<<num[b];
return 0;
}
注意两个地方:一个是memset-1那里要先初始化数组
还有一个是第一个出发点要给值,包括num=0和vis=1
bfsP1443 马的遍历
队列:
入队:如q.push(x):将x元素接到队列的末端;
出队:如q.pop() 弹出队列的第一个元素,并不会返回元素的值;
访问队首元素:如q.front()
访问队尾元素:如q.back();
访问队中的元素个数:如q.size();
判断队列是否为空:q.empty()
memset函数:
void *memset(void *s, int ch, size_t n);
函数解释:将s中当前位置后面的n个字节用 ch 替换并返回 s 。作用是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法
代码实现:
#include<iostream>
#include<queue>
#include<algorithm>
#include<string.h>
using namespace std;
int n,m,x,y;//n*m的棋盘,在(x,y)处有一个马
queue<pair<int,int> >q;//定义一个队列
int a[405][405];//记录每个点所用步数
int vis[405][405];//记录每个点是否走过
int nx[9]={0,2,2,1,1,-1,-1,-2,-2};//x方向的走法
int ny[9]={0,1,-1,2,-2,2,-2,1,-1};//y方向的走法
void bfs(){
while(!q.empty())//在队列中还有元素时
{
x=q.front().first;//先访问队首元素,由于是一个pair容器,通过.first访问到第一个元素即x
y=q.front().second;//访问到当前的y
q.pop();//把存下来的x和y这一对点出队列
for(int i=1;i<=8;i++){
int fx=x+nx[i];
int fy=y+ny[i];
if(fx>=1&&fy>=1&&fx<=n&&fy<=m&&vis[fx][fy]==0)
{
vis[fx][fy]=1;
q.push(make_pair(fx,fy));//把下一个数入队列
a[fx][fy]=a[x][y]+1;//对应每次的入队出队,进队的那个数即走到的那个数应该比出队的那个数步数多1
}
}
}
}
int main(){
cin>>n>>m>>x>>y;
memset(a,-1,sizeof(a));
a[x][y]=0;vis[x][y]=1;
q.push(make_pair(x,y));
bfs();
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++)printf("%5d",a[i][j]);
cout<<endl;
}
return 0;
}
P1141 01迷宫
第一次完全做的时候如下,用dfs做的,也没有考虑所给数据存迷宫时的数字连在一起了
后来改了一下之后:
自己给数据测试基本上是对的,方法肯定没问题。
#include<iostream>
#include<string.h>
using namespace std;
const int N=1e3+5;
int n,m;//n*n的迷宫 m个需要求的数据
char a[N][N];//存迷宫
int ans;//存答案
int vis[N][N]={0};//记录是否走过
int v[N][N]={0};//记录所有走过的点
int xx[4]={0,0,1,-1};
int yy[4]={1,-1,0,0};
void dfs(int x,int y){
for(int i=0;i<4;i++){
if(x+xx[i]>=0&&x+xx[i]<n&&y+yy[i]>=0&&y+yy[i]<n&&!vis[x+xx[i]][y+yy[i]]&&a[x][y]!=a[x+xx[i]][y+yy[i]])
{
vis[x+xx[i]][y+yy[i]]=1;
v[x+xx[i]][y+yy[i]]=1;
dfs(x+xx[i],y+yy[i]);
vis[x+xx[i]][y+yy[i]]=0;
}
}
}
int main(){
cin>>n>>m;
for(int i=0;i<n;i++)
cin>>a[i];
for(int i=0;i<m;i++){
int x,y;
cin>>x>>y;
x--;y--;
ans=0;
memset(vis,0,sizeof(vis));
memset(v,0,sizeof(v));
vis[x][y]=1;
v[x][y]=1;
dfs(x,y);
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
if(v[i][j]==1)ans++;
cout<<ans<<endl;
}
return 0;
}
不知道为啥超时但是还是超时了,下面换bfs做:
#include<iostream>
#include<string.h>
#include<queue>
using namespace std;
const int N=1e3+5;
int n,m;//n*n的迷宫 m个需要求的数据
char a[N][N];//存迷宫
int ans;//存答案
int vis[N][N]={0};//记录是否走过
int v[N][N]={0};//记录所有走过的点
int xx[4]={0,0,1,-1};
int yy[4]={1,-1,0,0};
queue<pair<int,int> >q;
void bfs(){
while(!q.empty()){
int x=q.front().first;
int y=q.front().second;
q.pop();
for(int i=0;i<4;i++){
if(x+xx[i]>=0&&x+xx[i]<n&&y+yy[i]>=0&&y+yy[i]<n&&!vis[x+xx[i]][y+yy[i]]&&a[x][y]!=a[x+xx[i]][y+yy[i]])
{
vis[x+xx[i]][y+yy[i]]=1;
v[x+xx[i]][y+yy[i]]=1;
q.push(make_pair(x+xx[i],y+yy[i]));
}
}
}
}
int main(){
cin>>n>>m;
for(int i=0;i<n;i++)
cin>>a[i];
for(int i=0;i<m;i++){
int x,y;
cin>>x>>y;
x--;y--;
ans=0;
memset(vis,0,sizeof(vis));
memset(v,0,sizeof(v));
q.push(make_pair(x,y));
vis[x][y]=1;
v[x][y]=1;
bfs();
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
if(v[i][j]==1)ans++;
cout<<ans<<endl;
}
return 0;
}
稍微好了那么一点点