广度优先搜索2018-08-14

这道题:https://vjudge.net/contest/246999#problem/D
这个显然就是一个BFS问题的模板,首先将输入的数都翻转(0->1 1->0)这样就可以潜移默化地将边界都设为0,输出就需要逆序输出,所以输出时我用的是递归方法而已,BFS是这样实现的:首先,一旦找到了一个是”1”,那就将它设为0,表示已经走过了,然后,再判断它的上下左右,一旦是”1”就将它也设为0,因为题目保证只有一个路径走法,所以我们只需要再存下走的坐标即可,同时,在运用方向数组。

#include <iostream>//注意,在POJ上,不能用万能头文件!!!(我表示被坑了好久)
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=1000+10;
int a[6][6],q[maxn*maxn][3],d[5][2]={{0,1},{1,0},{-1,0},{0,-1}};//初始化,d数组表示4个方向的不同坐标
void dg(int x){//递归输出函数
    if(x==0)//如果是0,就自然退出输出
       return ;
    dg(q[x][2]);//继续输出下一个坐标
    printf("(%d, %d)\n",q[x][0]-1,q[x][1]-1);//打印
}
int main(){
    int s=0,t=1,k;
    for(int i=1;i<=5;i++)//输入5*5数组
        for(int j=1;j<=5;j++){
            cin>>k;
            a[i][j]=1-k;//这里很关键!将1变成0,0变成1,潜移默化地将四周建立了边界(超过5*5的都为0)
        }
    q[1][1]=q[1][0]=1;//先将前面两个设为1
    a[1][1]=0;//从(1,1)开始走,先将(1,1)设为0,表示已走
    while(s<t){//正式开始BFS,当头<尾
        s++;//向后移一位
        if(q[s][0]==5&&q[s][1]==5){//如果到达了终点(5,5),就直接跳出
            k=s;
            break;
        }
        for(int i=0;i<4;i++){//这里是判断四个方位,即上下左右
            int xx=q[s][1]+d[i][1];//x坐标
            int yy=q[s][0]+d[i][0];//y坐标
            if(a[xx][yy]){//如果这个数(xx,yy)可以走
                q[++t][0]=xx;//横坐标更新
                q[t][1]=yy;//纵坐标更新(注意,这里t不能加1)
                a[xx][yy]=0;//标位已走
                q[t][2]=s;//存下正确步骤
            }
        }
    }
    dg(k);//直接调用k,从k开始输出
    return 0;//一个好的程序猿评判标准
}

传送门:https://vjudge.net/contest/246999#problem/E
这一道题可以用DFS也可以用BFS,不过今天学的是BFS那就用BFS吧,跟上一题类似,这一道题只不过是在上一道模板题再增加了计数器的使用,只要定义一个ans存答案就可以了,我甚至觉得这一题还是比上一题简单·······这一题就是每找到一个水域就向上下左右扩展,并如果可以继续扩张(上下左右左上左下右上右下为1),就继续设为已灌水(QAQ),那么,这道题分析到这里了,上代码:

#include <iostream>//坑人的POJ,不能用万能头文件
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=100+5,maxm=10000+10;
int n,m,s,t,ans,a[maxn][maxn],q[maxm][2],d[9][2]={{1,1},{1,0},{1,-1},{0,-1},{0,1},{-1,0},{-1,1},{-1,-1}};//初始化定义坐标数组
char c;//一个字母
int main(){ 
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){//输入,如果是"W"就代表有水,设为1,否则就设为0。和上一题一样,这种操作同样潜移默化地将四周设置了一层无形的屏障,如果超过n行m列,就都是0,走无可走。
            cin>>c;
            a[i][j]=c=='W'?1:0;//三目,也可以如下写:
            /*
            if(c=='W')
               a[i][j]=1;
            else
               a[i][j]=0;
            */
        }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(a[i][j]){//如果a[i][j]还没有被灌水,那就灌水吧!!!
                ans++;//计数器(存储找到的湖泊总数)
                a[i][j]=0;//设为已经走过
                q[1][1]=j;//坐标初始化
                q[1][0]=i;
                s=0;//队列首尾初始化
                t=1;
                while(s<t){//如果首<尾,BFS
                    s++;//首加一
                    for(int k=0;k<8;k++){//向四周的8个方向扩展灌水
                        int xx=q[s][0]+d[k][0];//存储当前的x坐标和y坐标
                        int yy=q[s][1]+d[k][1];
                        if(a[xx][yy]){//如果没有灌过,就继续灌
                            q[++t][0]=xx;//同样存下新一轮坐标
                            q[t][1]=yy;
                            a[xx][yy]=0;//设为已经灌过水了
                            //和上一题不同的是,这里不需要输出步骤,只需要算出湖泊总数即可,那么也就不需要多定义一个小的单元数组
                        }
                    }
                }
            }
    cout<<ans;//输出
    return 0;//一个好的程序猿评判标准
}

第三题:https://www.luogu.org/problemnew/show/P1588
这一题我运用到的是一些队列的知识,什么?你没有学过队列?那么,你应该安利一下我们教练的blog:cnyali->真正的大佬:传送门https://blog.csdn.net/cnyali/article/details/78091763
由于老师给的题目没有要输入n组这个条件,所以这里也不用加n组数据了。
这里用到了队列的知识,首先,需要特判一下,如果奶牛在农夫的前面,可怜的农夫就只能直接一步一步退后那就只要输出奶牛和农夫的距离之差即可。程序一开始将n入队,然后分别考虑农夫的三种走法:+1,-1,*2,当队列不空,就广搜三种走法,先将队列的第一个数提取出来存在x中(具体操作:q.front()),再将它原先位置提出队列(q.pop),如果x就直接等于m,也就是农夫抓到了逃跑的奶牛(fkq),直接输出即可,否则,如果三种状态没有超过最大限制,继续BFS三种情况(+1,-1,*2)
代码如下:

#include <bits/stdc++.h>//终于摆脱了万恶的POJ,可以用万能头文件了!!!23333
using namespace std;
const int maxn=200000+10;
int f[maxn],n,m;
queue<int>q;//定义一个名字为q的整形队列
int main(){
    int n,m;
    cin>>n>>m;
    if(m<=n){//特判,如果农夫在奶牛前面,那么,农夫只能一步步倒退
        cout<<n-m;
        return 0;
    }
    for(int i=1;i<=maxn;i++)//首先,将所有可能的走到的地方都设为-1,表示都没有走过
        f[i]=-1;
    f[n]=0;//那么,第n个就该设为0
    q.push(n);//先将n入队
    while(!q.empty()){//当队列q不空
        int x=q.front();//将q的第一个数提取出来,存在x里面
        q.pop();//将原来的第一个数的位置提出
        if(x==m){//如果已经抓到了奶牛(fkq),直接输出
            cout<<f[x];
            return 0;
        }
        if(x*2<=maxn&&f[x*2]==-1){//如果可以往*2的方向走,那么需要判断那里有没有被走过,因为如果没有这个条件,那么农夫可能一直来回跳动,一直跳到世界毁灭·····还有要判断会不会超界
            q.push(x*2);//将x*2入队
            f[x*2]=f[x]+1;//标记
        }
        if(x+1<=maxn&&f[x+1]==-1){//如上
            q.push(x+1);
            f[x+1]=f[x]+1;
        }
        if(x>1&&f[x-1]==-1){//这里第一个条件有所不同,如果x-1>0,就是x-1可以走,其他的还是一样
            q.push(x-1);
            f[x-1]=f[x]+1;
        }
    }
    return 0;//一个好的程序猿评判标准
}

猜你喜欢

转载自blog.csdn.net/qq_42875611/article/details/81675113