【DFS+BFS】2019 GDUT Winter Training I

同步发布于My Blog
看到各位dalao都把题解和cf补完了,心里慌得一批,马上gun来补题解总结(〃‘▽’〃)
专题链接


BFS(Breadth-first search):

顾名思义,广度优先搜索即优先各个方向向外扩张,而不是专注于某个方向上的深度(DFS),  
既然如此,那么BFS一层一层向外延伸的特点可用于求解最短路问题
下面几道例题调教一下吧( ̄▽ ̄)/

K:Safe Path

大意:给定nm(2<=nm<=2e5)的地图,可上下左右移动,求从S点到F点的最短路,同时地图有若干
怪兽M,其(曼哈顿距离)[]为d(0<=d<=2e5)的点均不可走

分析:如果题目没有怪兽的条件,那么就是一道BFS裸题,除了需要考虑如何存地图(*1).  
存在怪兽的情况下,对于每个怪兽如果全部暴力标记不能走的点,那么存在一群怪兽聚在一起  
时,会对于某些点进行多次标记的多余操作,直接T.(*2)
解决办法:
(*1)·2e5*2e5二维数组开不下,可以转换为2e5一维数组.  
    ·因为输入数据是字符串,也可以考虑string存图.(STL大法好)
    整数的话可以用vector.
(*2)我们回想bfs的性质,如果从每个牛同时开始BFS进行标记,那么某两个牛的重合区域  
    将只会被其中一只牛标记,那么可以保证整张图每个点最多只被标记一次

代码如下
代码里面把每个点的4个方位最远点存进去是煞笔操作
简洁写法是记录每个怪兽坐标,最后一起丢进queue里面进行BFS即可

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <map>
#include <string.h>
using namespace std;

const int maxn = 2e5+5;
string a[maxn];
int n,m,d,sx,sy,fx,fy;
int dir[5]={0,-1,0,1,0};
queue<pair<pair<int,int>,int> > q;
map<pair<int,int>,int> ha;

int main() {
    scanf("%d %d %d",&n,&m,&d);
    getchar();
    for(int i=0;i<n;i++) {
        cin>>a[i];
        getchar();
    }
    for(int i=0;i<n;i++) {
        for(int j=0;j<m;j++) {
            if(a[i][j]=='S') {
                sx=i,sy=j;
            }else if(a[i][j]=='F'){
                fx=i,fy=j;
            }else if(a[i][j]=='M') {
                q.push(make_pair(make_pair(i,j),1));
                //shabi操作
                if(i+d<n) q.push(make_pair(make_pair(i+d,j),1)),ha[make_pair(i+d,j)]=1;
                if(i-d>=0) q.push(make_pair(make_pair(i-d,j),1)),ha[make_pair(i-d,j)]=1;
                if(j+d<n) q.push(make_pair(make_pair(i,j+d),1)),ha[make_pair(i,j+d)]=1;
                if(j-d>=0) q.push(make_pair(make_pair(i,j-d),1)),ha[make_pair(i,j-d)]=1;
                
                ha[make_pair(i,j)]=1;
                while(!q.empty()) {
                    int x=q.front().first.first;
                    int y=q.front().first.second;
                    q.pop();
                    for(int k=-1;k<=1;k++) {
                        for(int kk=-1;kk<=1;kk++) {
                            int nx=x+k,ny=y+kk;
                            if(nx>=0&&nx<n&&ny>=0&&ny<m&&!ha.count(make_pair(nx,ny))&&a[nx][ny]!='M'&&abs(nx-i)+abs(ny-j)<=d) {
                                //printf("%d %d %d %d\n",nx,ny,i,j);
                                a[nx][ny]='O';
                                ha[make_pair(nx,ny)]=1;
                                q.push(make_pair(make_pair(nx,ny),1));
                            }
                        }
                    }
                }
            }
        }
    }
/*
    for(int i=0;i<n;i++) {
        for(int j=0;j<n;j++) {
            cout<<a[i][j];
        }
        cout<<endl;
    }
*/
    
    if(a[sx][sy]=='S'&&a[fx][fy]=='F') {
        q.push(make_pair(make_pair(sx,sy),1));
        ha[make_pair(sx,sy)]=1;
        while(!q.empty()){
            int x=q.front().first.first;
            int y=q.front().first.second;
            int lv=q.front().second;
            //cout<<x<<" "<<y<<endl;
            q.pop();
            for(int i=0;i<4;i++) {
                int nx=x+dir[i],ny=y+dir[i+1];
                if(nx>=0&&nx<n&&ny>=0&&ny<m&&!ha.count(make_pair(nx,ny))&&a[nx][ny]!='O'&&a[nx][ny]!='M') {
                    if(a[nx][ny]=='F') {
                        cout<<lv<<endl;
                        return 0;
                    }
                    ha[make_pair(nx,ny)]=1;
                    q.push(make_pair(make_pair(nx,ny),lv+1));
                }
            }
        }
        cout<<-1<<endl;
    }else{
        printf("-1\n");
    }
    
    return 0;
}

L:Find The Multiple

大意:给定整数n(1<=n<=200),求G=m*n只存在1 or 0,(m<=1e100),输出G(不唯一)

分析:如果按照题意遍历n的倍数 ==> 1e100/1e2=1e98 ==> TLE
     反过来想,如果只遍历所有只有1或0的整数,那么约有2^100≈1e30
     打表之后发现似乎并不需要这么多,long long就能装下了,不会证明,逃~~
写法:维护一个queue,先push(1),每次取队头x,x*10和x*10+1继续push进去,直到找到  
     能整除n的整数,输出,结束

代码如下:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <string.h>
using namespace std;

typedef long long ll;
int n;
queue<ll> q;

int main() {
    while(scanf("%d",&n)&&n>0) {
        while(!q.empty()) q.pop();
        q.push(1);
        while(!q.empty()) {
            ll u=q.front();
            q.pop();
            if(u%n==0) {
                printf("%lld\n",u);
                break;
            }
            q.push(u*10);
            q.push(u*10+1);
        }
    }
    return 0;
}

M:Prime Path

大意:给定一个四位数,每次修改其中一位,求到达一个素数的最短路径

分析:涉及素数,那就先打出素数表
     那么可以模拟题意进行BFS,每次取队头,暴力出所有可以换的数字  
     (注意第一位不能为0),存进队列

代码如下:

#include <iostream>
#include <algorithm>
#include <string.h>
#include <queue>
#include <cstdio>
#include <cmath>
using namespace std;

const int maxn = 1e5;
int T,p[maxn],vis[maxn],s,f;
queue<pair<int,int> > q;

void init() {
    memset(p,0,sizeof(p));
    int m=sqrt(maxn+0.5);
    for(int i=2;i<=m;i++) if(!p[i]) {
        for(int j=i*i;j<=maxn;j+=i) p[j]=1;//1表示不是_(:з」∠)_ 
    }
}

int fun(int a,int n) {
    int u=1;
    for(int i=1;i<n;i++) {
        a/=10;
        u*=10;
    }
    return a%10*u;
}

int mypow(int x,int y) {
    int u=1;
    for(int i=1;i<=y;i++) {
        u*=x;
    }
    return u;
}

int main() {
    init();
    scanf("%d",&T);
    while(T--) {
        memset(vis,0,sizeof(vis));
        scanf("%d %d",&s,&f);
        int ok=0;
        while(!q.empty()) q.pop();
        q.push(make_pair(s,0));
        vis[s]=1;
        while(!q.empty()) {
            int nownum=q.front().first;
            int nowlv=q.front().second;
            //printf("%d %d\n",nownum,nowlv);
            q.pop();
            if(nownum==f) {
                cout<<nowlv<<endl;
                ok=1;
                break;
            }
            for(int i=1;i<=4;i++) {
                int c=nownum;
                c-=fun(c,i);
                //printf("%d %d\n",nownum,c);
                for(int j=0;j<=9;j++) {
                    if(c+j*mypow(10,i-1)>1000&&!vis[c+j*mypow(10,i-1)]&&!p[c+j*mypow(10,i-1)]) {
                        q.push(make_pair(c+j*mypow(10,i-1),nowlv+1));
                        vis[c+j*mypow(10,i-1)]=1;
                    }
                }
            }
        }
        if(!ok) {
            printf("Impossible\n");
        }
    }
    return 0;
}

DFS(Depth-first search)

深度优先遍历,有别于广度优先,每次搜索都会“一条路走到黑”,可以用于求解  
是否存在可行解,配合回溯、剪枝更美味哦Ψ( ̄∀ ̄)Ψ

G:Lake Counting

大意:给定n*m的地图,求w联通块数量,连通的条件是8个方向上有接触.

分析:裸DFS,注意一下标准写法。

代码如下:

#include <cstdio>
#include <iostream>
#include <string.h>
#include <algorithm>
#include <cstring>
using namespace std;

const int maxn = 100;
int ans=0,n,m,vis[maxn][maxn];
char map[maxn][maxn];
int dir[3]={0,-1,1};

int dfs(int x,int y) {
    for(int i=0;i<3;i++) {
        for(int j=0;j<3;j++) {
            int nx=x+dir[i],ny=y+dir[j];
            if(nx>=0&&ny>=0&&nx<n&&ny<m&&!vis[nx][ny]&&map[nx][ny]=='W'){
                vis[nx][ny]=1;
                dfs(nx,ny);
            }
        }
    }
    return 0;
}

int main() {
    memset(vis,0,sizeof(vis));
    scanf("%d %d",&n,&m);
    getchar();
    for(int i=0;i<n;i++) {
        gets(map[i]);
    }
    for(int i=0;i<n;i++) {
        for(int j=0;j<m;j++) {
            if(!vis[i][j]&&map[i][j]=='W') {
                ans++;
                vis[i][j]=1;
                dfs(i,j);
            }
        }
    }
    cout<<ans<<endl;
    return 0;
}

I:Red and Black

大意:给定N*M的地图,求出起点@能往外走的格子个数

分析:dfs裸题,找到起点,开始dfs递归,标记走过的格子,最后统计一下有多少个  
标记即可

代码如下:

#include <cstdio>
#include <iostream>
#include <string.h>
#include <algorithm>
#include <cstring>
using namespace std;

const int maxn = 50;
int ans=0,n,m,vis[maxn][maxn];
char map[maxn][maxn];
int dir[5]={0,-1,0,1,0};

int dfs(int x,int y) {
    for(int i=0;i<4;i++) {
        int nx=x+dir[i],ny=y+dir[i+1];
        if(nx>=0&&ny>=0&&nx<n&&ny<m&&!vis[nx][ny]&&map[nx][ny]=='.'){
            vis[nx][ny]=1;
            dfs(nx,ny);
        }
    }
    return 0;
}

int main() {
    while(scanf("%d %d",&m,&n)&&n>0&&m>0) {
        memset(vis,0,sizeof(vis));
        getchar();
        for(int i=0;i<n;i++) {
            gets(map[i]);
        }
        for(int i=0;i<n;i++) {
            for(int j=0;j<m;j++) {
                if(map[i][j]=='@') {
                    vis[i][j]=1;
                    dfs(i,j);
                }
            }
        }
        ans=0;
        for(int i=0;i<n;i++) {
            for(int j=0;j<m;j++) {
                if(vis[i][j]) {
                    ans++;
                }
            }
        }
        cout<<ans<<endl;
    }
    
    return 0;
}

H:Sticks

还没补or2

J:AND Graph

同样没做(/ω\)


后记:专题一的简单总结结束,专题二的DP继续加油啊WiLe蒟蒻∠( °ω°)/

配图

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值