BFS/DFS浅谈

学习篇(搜索)

先举个例子:遍历二叉树
(树的遍历层次)算法思想:维护一个队列,用于存放节点信息。当访问到一个节点的时候,先访问该节点,再将该节点出队,再将其左右儿子分别放入队列。在这里插入图片描述

再来讲讲搜索
起初我做搜索题真的是一头雾水,直接放弃

搜索的本质:暴力枚举(让程序从一个状态跳到另一个状态就是搜索。)

只要把问题,能够表示出他的状态,并且从一个状态变化到另一个状态的规则能够定义出来,就能够去搜索

再举个例子体会一下搜索
hdu 1548
题意:有一个奇怪的电梯,只有两个按钮:上,下,并且,当在i层的时候,按上就会上ki层,按下就会下ki层,电梯不高于N,也不低于1;问:当你再A层要去B层,至少要按上或下多少次。

状态转移: 把楼层编号和按键钮的次数的信息作为搜索的状态,再按照题目的意思/规则,在各个状态之间变化,这个状态之间的变化就叫状态转移。
(A层,0) ->···->( B层,x)
A层 到下一个状态有 上ki 或 下ki 并且次数加1
不难看出这一题的状态转移图是二叉树类型的

BFS

基本思想:
从初始状态开始,利用规则,生成所有可能的状态,构成树的下一层节点。
检查是否出现目标状态G,若未出现,就对该层所有状态节点,分别依次利用规则,生成再下一层的所有状态。
对新一层的所有状态节点继续检查是否出现G,若未出现,继续按照上面的思想继续生成下一层的所有节点,这样一层一层往下展开,直到目标状态出现为止。

**他的优化:**对状态作标记

在这里插入图片描述
板子:请看下面的题(前两题足够了

DFS

关键:解决“当下该如何做”!
至于下一步怎么做,则与当前一样,只是参数不一样而已

基本模型:
在这里插入图片描述

就是递归:题做多了就知道要相信自己的递归函数,不用去想下一层

水题集

BFS

hdu 1548:二叉树型搜索

hdu 1548

#include <bits/stdc++.h>
#define ll long long
using namespace std;

//"二叉树型的状态转移"
int A,B,N,a[210],v[210];
struct node{int l,s;};//l楼层,s层数
void bfs(){
    node cur,nex;
    cur.l=A; cur.s=0;//初始化
    queue<node>q;//创建队列
    q.push(cur);//父节点入队
    v[A]=1;//标记
    while(!q.empty()){
        cur=q.front();
        q.pop();//队头出队
        if(cur.l==B){//找到目标状态
            cout<<cur.s<<endl;return;
        }
        //下一层状态1
        nex.l=cur.l+a[cur.l];
        nex.s=cur.s+1;
        if(nex.l<=N && !v[nex.l]){//符合条件入队
            q.push(nex);v[nex.l]=1;
        }
        //下一层状态2
        nex.l=cur.l-a[cur.l];
        nex.s=cur.s+1;
        if(nex.l>=1 && !v[nex.l]){
            q.push(nex);v[nex.l]=1;
        }
    }
    cout<<"-1"<<endl;//无法到达目标状态
    return ;
}

int main(){
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    while(cin>>N){
        if(N==0)break;
        cin>>A>>B;
        for(int i=1;i<=N;i++){cin>>a[i];v[i]=0;}//标记数组初始化
        bfs();
    }
    return 0;
}

hdu 1459:三个杯子倒可乐

hdu 1459

TLE 了一次(我感觉hdu的测评有点问题,应该是WA才对,之前也遇到这种情况):如果S为奇数的话,就直接输出-1.这种情况没考虑。

#include <bits/stdc++.h>
using namespace std;

int A,B,C,v[110][110],lev;//用二维数组标记就行了,三维也行
struct node {int a,b,c,s;};
void bfs(){
    node cur,nex;
    cur.a=A; cur.b=cur.c=cur.s=0; v[cur.a][cur.b]=1;//初始状态的信息输入
    queue<node>q;//搞个队列
    q.push(cur);//初始状态入队
    while(!q.empty()){
        cur=q.front();
        q.pop();//父节点出队
        if(cur.a==A/2&&A%2==0){//目标状态出现
            cout<<cur.s<<endl;return ;
        }
        lev=B-cur.b;//a->b
        if(cur.a&&lev){
            if(cur.a>=lev){nex.a=cur.a-lev;nex.b=B;}
            else {nex.a=0;nex.b=cur.a+cur.b;}
            nex.c=cur.c;nex.s=cur.s+1;
            if(!v[nex.a][nex.b]){
                q.push(nex);v[nex.a][nex.b]=1;
            }
        }
        lev=C-cur.c;//a->c
        if(cur.a&&lev){
            if(cur.a>=lev){nex.a=cur.a-lev;nex.c=C;}
            else {nex.a=0;nex.c=cur.a+cur.c;}
            nex.b=cur.b;nex.s=cur.s+1;
            if(!v[nex.a][nex.b]){
                q.push(nex);v[nex.a][nex.b]=1;
            }
        }
        lev=B-cur.b;//c->b
        if(cur.c&&lev){
            if(cur.c>=lev){nex.c=cur.c-lev;nex.b=B;}
            else {nex.c=0;nex.b=cur.c+cur.b;}
            nex.a=cur.a;nex.s=cur.s+1;
            if(!v[nex.a][nex.b]){
                q.push(nex);v[nex.a][nex.b]=1;
            }
        }
        lev=A-cur.a;//c->a
        if(cur.c&&lev){
            if(cur.c>=lev){nex.c=cur.c-lev;nex.a=A;}
            else {nex.c=0;nex.a=cur.c+cur.a;}
            nex.b=cur.b;nex.s=cur.s+1;
            if(!v[nex.a][nex.b]){
                q.push(nex);v[nex.a][nex.b]=1;
            }
        }

        lev=C-cur.c;//b->c
        if(cur.b&&lev){
            if(cur.b>=lev){nex.b=cur.b-lev;nex.c=C;}
            else {nex.b=0;nex.c=cur.b+cur.c;}
            nex.a=cur.a;nex.s=cur.s+1;
            if(!v[nex.a][nex.b]){
                q.push(nex);v[nex.a][nex.b]=1;
            }
        }

        lev=A-cur.a;//b->a
        if(cur.b&&lev){
            if(cur.b>=lev){nex.b=cur.b-lev;nex.a=A;}
            else {nex.b=0;nex.a=cur.b+cur.a;}
            nex.c=cur.c;nex.s=cur.s+1;
            if(!v[nex.a][nex.b]){
                q.push(nex);v[nex.a][nex.b]=1;
            }
        }
    }
    cout<<"NO"<<endl;return ;
}


int main(){
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    while(cin>>A>>B>>C){
        if(A==0&&B==0&&C==0)break;
        memset(v,0,sizeof(v));
        bfs();
    }
    return 0;
}

hdu 1372:骑士起点到终点最少步数

hdu 1372

Output Limit Exceeded 了一次:while里直接scanf()啥也不加的情况,我再也不敢了

#include <bits/stdc++.h>
using namespace std;
//这里才发现 string s;这样的不能用scanf("%s")输入
int sx, sy, x, y, v[70][70]; char s1[5], s2[5];
int dy[8] = { 2,1,-1,-2,-2,-1,1,2 };
int dx[8] = { 1,2,2,1,-1,-2,-2,-1 };
struct node { int x, y, s; };

int bfs() {
	node cur, nex;
	cur.x = sx; cur.y = sy; cur.s = 0; v[cur.x][cur.y] = 1;
	queue<node>q;
	q.push(cur);
	while (!q.empty()) {
		cur = q.front();
		q.pop();
		if (cur.x == x && cur.y == y) {
			return cur.s;
		}

		for (int i = 0; i < 8; i++) {
			nex.x = cur.x + dx[i]; nex.y = cur.y + dy[i];
			if (!v[nex.x][nex.y] && nex.x >= 1 && nex.x <= 8 && nex.y >= 1 && nex.y <= 8) {
				nex.s = cur.s + 1; q.push(nex);
				v[nex.x][nex.y] = 1;
			}
		}
	}
}

int main() {//cin>>c>>a>>d>>b
	//ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	while (scanf("%s%s", s1, s2) == 2) {//好坑啊这里,我习惯while里直接scanf别的什么都不加,结果一直Output Limit Exceeded
		sy = s1[0] - 96; y = s2[0] - 96; sx = s1[1] - '0'; x = s2[1] - '0';
		memset(v, 0, sizeof(v));
		printf("To get from %s to %s takes %d knight moves.\n", s1, s2, bfs());
	}
	return 0;
}

hdu 1242:类似起点到终点题

hdu 1242

#include <bits/stdc++.h>
using namespace std;

struct node{int x,y,t;};
int g[210][210],sx,sy,x,y,v[210][210],n,m;
int dy[4]={1,0,-1,0};
int dx[4]={0,1,0,-1};

void bfs(){
    node cur,nex;
    cur.x=sx;cur.y=sy;cur.t=0;v[cur.x][cur.y]=1;
    queue<node>q;
    q.push(cur);
    while(!q.empty()){
        cur=q.front();
        q.pop();
        if(cur.x==x&&cur.y==y){
            cout<<cur.t<<endl;return ;
        }

        for(int i=0;i<4;i++){
            nex.x=cur.x+dx[i];nex.y=cur.y+dy[i];
            if(g[nex.x][nex.y]=='#'||nex.x>n||nex.x<1||nex.y<1||nex.y>m)continue;
            nex.t=cur.t+1;
            if(g[nex.x][nex.y]=='x')nex.t+=1;

            if(!v[nex.x][nex.y]){
                q.push(nex);v[nex.x][nex.y]=1;
            }
        }


    }
    cout<<"Poor ANGEL has to stay in the prison all his life."<<endl;return;
}


int main(){
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    while(cin>>n>>m){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                char a;cin>>a;
                g[i][j]=a;
                if(a=='a'){x=i;y=j;}
                if(a=='r'){sx=i;sy=j;}
                v[i][j]=0;
            }
        }
        bfs();

    }
    return 0;
}

hdu 1253:类起点到终点(三维)

hdu 1253

wa了一次:没考虑到他有可能永远也到不了出口的情况

#include <bits/stdc++.h>
using namespace std;

int A,B,C,T;
int g[60][60][60],v[60][60][60];
int da[6]={0,1, 0,-1,0, 0};
int db[6]={1,0,-1, 0,0, 0};
int dc[6]={0,0, 0, 0,1,-1};
struct node {int a,b,c,t;};

void bfs(){
    node cur,nex;
    cur.a=cur.b=cur.c=cur.t=0;v[cur.a][cur.b][cur.c]=1;
    queue<node>q;
    q.push(cur);
    while(!q.empty()){
        cur=q.front();
        q.pop();
        if(cur.a==A-1&&cur.b==B-1&&cur.c==C-1){
            printf("%d\n",(cur.t<=T?cur.t:-1));return;
        }

        for(int i=0;i<6;i++){
            nex.a=cur.a+da[i];nex.b=cur.b+db[i];
            nex.c=cur.c+dc[i];nex.t=cur.t+1;
            if( nex.a<A&&nex.a>=0&&nex.b<B&&nex.b>=0&&nex.c>=0&&nex.c<C  && !v[nex.a][nex.b][nex.c] && g[nex.a][nex.b][nex.c]==0 ){
                q.push(nex);v[nex.a][nex.b][nex.c]=1;
            }
        }
    }
    printf("-1\n");return ;
}


int main(){
    int t;scanf("%d",&t);
    while(t--){
        scanf("%d%d%d%d",&A,&B,&C,&T);
        for(int i=0;i<A;i++){
            for(int j=0;j<B;j++){
                for(int k=0;k<C;k++){
                    scanf("%d",&g[i][j][k]);
                    v[i][j][k]=0;
                }
            }
        }
        bfs();
    }
    return 0;
}

DFS

hdu 1241:油坑个数

hdu 1241

题意:求相邻小油坑构成的大油坑个数
注意输入

#include <bits/stdc++.h>
using namespace std;

int n,m,ans;char mp[110][110];
int dx[8]={1,-1,0,0, -1,-1,1,1};
int dy[8]={0,0,1,-1, -1, 1,1,-1};

void dfs(int x,int y){
    //dfs就是一层的状态;如果不知道出口是什么就想想最后一层
    //最后一个状态一定是四周都是*,所以出口在循环完之后
    for(int i=0;i<8;i++){
        int tx=x+dx[i],ty=y+dy[i];
        if(mp[tx][ty]=='*'||tx<1||ty<1||tx>m||ty>n)continue;
        mp[tx][ty]='*';
        dfs(tx,ty);//连通图不需要回溯
    }

    return ;
}

int main(){
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    while(cin>>m>>n){
        if(m==0)break;

        for(int i=1;i<=m;i++){
            for(int j=1;j<=n;j++){
                char a;cin>>a;
                mp[i][j]=a;
            }
        }
        ans=0;
        for(int i=1;i<=m;i++){
            for(int j=1;j<=n;j++){
                if(mp[i][j]=='@'){
                    mp[i][j]='*';
                    dfs(i,j);ans++;
                }
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

wa了多次

DFS

hdu 1010:迷宫类恰好到出口

hdu 1010

题意:每走一步花费一秒,并且走过的格子不能再走,求是否能从起点恰好走到终点。

tle的原因:void的dfs中第一行的判断边界条件错了
re的原因:一些编译细节字母打错了(该死,找了好久才找到
难点:
剪枝:可以在dfs前剪枝
1、走完所有可走的路用的时间 小于 T -> NO
2、"两点最短的路径"用的时间 比 T大 -> NO
3、奇偶剪枝:
因为从起点到终点 的任何路径的步数的奇偶性相同(可以用“最短路”的值的奇偶性表示)
所以,若t秒 和 路径时间 的奇偶性不一致(同性相减为偶数) ->NO

#include <bits/stdc++.h>
using namespace std;

int N,M,T,sx,sy,x,y,mi;char mp[10][10];bool f;
int dx[4]={1,-1,0,0};
int dy[4]={0,0,1,-1};

void dfs(int sx,int sy,int s){
    if(sx>N||sy>M||sx<1||sy<1)return ;//这里是||啊
    if(sx==x&&sy==y&&s==T) f=1;//结束条件
    if(f) return;
    for(int i=0;i<4;i++){
        int tx=sx+dx[i],ty=sy+dy[i];
        if(mp[tx][ty]=='X'||tx<=0||tx>N||ty<=0||ty>M)continue;
        mp[tx][ty]='X';
        dfs(tx,ty,s+1);
        mp[tx][ty]='.';
    }
    return;
}

int main(){
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    while(cin>>N>>M>>T){
        if(M==0&&N==0&&T==0)break;
        int wall=0;
        for(int i=1;i<=N;i++){
            for(int j=1;j<=M;j++){
                char a;cin>>a;
                mp[i][j]=a;
                if(a=='S'){sx=i;sy=j;}
                else if(a=='D'){x=i;y=j;}
                else if(a=='X')wall++;
            }
        }
        mi=abs(sy-y)+abs(sx-x);
        if(N*M-wall<=T || mi>T || (T-mi)%2!=0 ){
            cout<<"NO"<<endl;continue;
        }
        mp[sx][sy]='X';
        f=0;
        dfs(sx,sy,0);
        if(f)cout<<"YES"<<endl;
        else cout<<"NO"<<endl;
    }
    return 0;
}
#include <bits/stdc++.h>
using namespace std;

int N,M,T,sx,sy,x,y,mi,temp;char mp[10][10];bool f;
int dx[4]={1,-1,0,0};
int dy[4]={0,0,1,-1};

bool dfs(int sx,int sy,int s){
    if(sx==x&&sy==y&&s==T)return 1;
    temp=T-s-abs(sx-x)-abs(sy-y);
    if(temp<0||temp%2==1)return 0;

    for(int i=0;i<4;i++){
        int tx=sx+dx[i],ty=sy+dy[i];
        if(mp[tx][ty]!='X'&&tx>0&&tx<=N&&ty>0&&ty<=M){
            mp[tx][ty]='X';
            if(dfs(tx,ty,s+1))return 1;
            mp[tx][ty]='.';
        }
    }
    return 0;
}


int main(){
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    while(cin>>N>>M>>T){
        if(M==0&&N==0&&T==0)break;
        int wall=0;
        for(int i=1;i<=N;i++){
            for(int j=1;j<=M;j++){
                char a;cin>>a;
                mp[i][j]=a;
                if(a=='S'){sx=i;sy=j;}
                else if(a=='D'){x=i;y=j;}
                else if(a=='X')wall++;
            }
        }
        mi=abs(sy-y)+abs(sx-x);
        if(N*M-wall<=T || mi>T || (T-mi)%2!=0 ){
            cout<<"NO"<<endl;continue;
        }
        mp[sx][sy]='X';
        if(dfs(sx,sy,0))cout<<"YES"<<endl;
        else cout<<"NO"<<endl;
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值