寒假ACM集训复习总结Day4-helman

这一天讲的是DFS,BFS深度优先搜索和广度优先搜索

就算是第二次再看题目,也觉得很难...

 

https://vjudge.net/contest/283487#problem/A

看着一遍源代码也说不出自己到底懂没有,只能说说看完代码后自己懂了多少

这种迷宫题是典型的广度搜索题,不同一般的广度搜索题的是,这道题需要用到两个图

意思就是一般的题是找一个标记为0的点后,不断向外标记,得出终点的标记值

这道题不仅要创一个以起点为起点的图,根据走的步数向外标记的图——简称人物图吧

///说起来,的确没考虑为什么一找到终点,此时的标记值就是最短距离呢,这是因为广度搜索的分支是同时进行的,找到即最短

还要创一个以怪物为起点的,根据怪物行走范围向外标记的图——简称怪物图吧

用怪物图来限制人物图的范围——就是人在人物图上走的时候怪物图上有标记的不能走啦

这应该就是初次做比较难想到的吧

#include <bits/stdc++.h>
using namespace std;
typedef pair<int ,int >P;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
const int INF=0x3f3f3f3f;
const int maxn=2e5+7;
vector<char >mpa[maxn];
vector<int >mp1[maxn],mp2[maxn];
char str[maxn];
int n,m,d,sx,sy,fx,fy;
queue<P>que;
int bfs(int a,int b){
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            mp2[i][j]=INF;
    mp2[a][b]=0;
    if(mp1[a][b]>d)que.push(P(a,b));
    while(!que.empty()){
        P now=que.front();
        que.pop();
        int x=now.first,y=now.second;
        if(x==fx&&y==fy)break;
        for(int i=0;i<4;i++){
            int xx=x+dx[i],yy=y+dy[i];
            if(xx<1||xx>n||yy<1||yy>m||mp2[xx][yy]!=INF||mp1[xx][yy]<=d)continue;
            mp2[xx][yy]=mp2[x][y]+1;
            que.push(P(xx,yy));
        }
    }
    return mp2[fx][fy];
}
int main(){
    //freopen("1.in","r",stdin);
    while(scanf("%d %d %d",&n,&m,&d)!=EOF){
        for(int i=1;i<=n;i++){
            mp1[i].resize(m+1);
            mp2[i].resize(m+1);
            mpa[i].resize(m+1);
        }
        for(int i=1;i<=n;i++){
            scanf("%s",str+1);
            mpa[i].push_back(0);
            for(int j=1;j<=m;j++){
                mpa[i][j]=str[j];
                if(str[j]=='S')sx=i,sy=j;
                if(str[j]=='F')fx=i,fy=j;
            }
        }
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                mp1[i][j]=INF;
                if(mpa[i][j]=='M'){mp1[i][j]=0;que.push(P(i,j));}
            }
        }
        while(!que.empty()){
            P now=que.front();
            que.pop();
            int x=now.first,y=now.second;
            for(int i=0;i<4;i++){
                int xx=x+dx[i],yy=y+dy[i];
                if(xx<1||xx>n||yy<1||yy>m||mp1[xx][yy]!=INF)continue;
                mp1[xx][yy]=mp1[x][y]+1;
                que.push(P(xx,yy));
            }
        }
        int ans=bfs(sx,sy);
        if(ans==INF)cout<<-1<<endl;
        else cout<<ans<<endl;
    }
    return 0;
}

 

https://vjudge.net/contest/283487#problem/B

这道就很简单了,只用一个图来标识

这是用赋值INF来表示没走过的

#include <iostream>
#include<queue>
#include<string.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=1e5+7;
int len[maxn];
int k,n;
queue<int >que;
int bfs(){
    int now,next;
    while(!que.empty()){
        now=que.front();
        que.pop();
        if(now==k)break;
        if(now-1>=0&&len[now-1]==INF){
            next=now-1;
            len[next]=len[now]+1;
            que.push(next);
        }
        if(now+1<=100000&&len[now+1]==INF){
            next=now+1;
            len[next]=len[now]+1;
            que.push(next);
        }
        if(now*2<=100000&&len[now*2]==INF){
            next=now*2;
            len[next]=len[now]+1;
            que.push(next);
        }
    }
    return len[now];
}
int main(){
    while(cin>>n>>k){
        while(!que.empty())
            que.pop();
        memset(len,INF,sizeof(len));
        len[n]=0;
        que.push(n);
        cout<<bfs()<<endl;
    }
    return 0;
}
View Code

还有个用vis数组来表示走没走过的,不过不是我写的,是做题后学长发的答案代码

#include <cstdio>           
#include <cstring>
#include <queue>
#include <iostream>
#include <algorithm>
using namespace std;
int vis[100005];
int n, k;
struct Node{
    int x,s;
};
queue<Node>q;

int bfs(){
    Node now, next;
    while (!q.empty()){
        now = q.front();
        q.pop();
        if (now.x == k)break;
        if (now.x - 1 >= 0 && !vis[now.x - 1]){
            next.x = now.x - 1;
            vis[next.x] = 1;
            next.s = now.s + 1;
            q.push(next);
        }
        if (now.x + 1 <=100000 && !vis[now.x + 1]){
            next.x = now.x + 1;
            vis[next.x] = 1;
            next.s = now.s + 1;
            q.push(next);
        }
        if (now.x *2 <= 100000 && !vis[now.x * 2]){
            next.x = now.x * 2;
            vis[next.x] = 1;
            next.s = now.s + 1;
            q.push(next);
        }
    }
    return now.s;
}

int main(){
    Node now;
    while (scanf("%d%d", &n, &k) != EOF){
        memset(vis, 0, sizeof(vis));
        while (!q.empty())q.pop();
        now.x = n;
        now.s = 0;
        vis[now.x] = 1;
        q.push(now);
        printf("%d\n", bfs());
    }
    return 0;
}
View Code

 

https://vjudge.net/contest/283487#problem/C

这道题有点怪,不求到达终点需要的步数,反而要我们把走的路径输出来

用一个结构体,在一个结构里除了装自己的行列还装上一个数的行列行不行

当然不行,这样虽然知道第一个位置就相当第二个位置,但第二个位置无法指向第三个位置

干脆形象一点,在一根绳子上打结,打的结就是走过的点

不过如何通过绳子找到之前的点呢,很简单,只要找上一个结就行了

这样的话绳子会有分叉,因为我们是让新产生的结指向原来的结,不是让原来的结只指向一个新的结

所以当这个结到达终点,就能通过这一条绳得到之前走过的点,当然,其他分支就都无效了

#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
int maze[5][5];
bool vis[5][5];
const int dx[]={0,0,1,-1};
const int dy[]={1,-1,0,0};
struct node{
    int x;int y;int pre;
};
node q[100];
void print(node s){
    if(s.pre!=-1)print(q[s.pre]);
    printf("(%d, %d)\n",s.x,s.y);
}
void bfs(){
    memset(vis,0,sizeof(vis));
    vis[0][0]=1;
    node s,t,next;
    int front=0,end=0;
    s.x=0,s.y=0,s.pre=-1;
    q[end++]=s;
    while(front<end){
        t=q[front++];
        int a=t.x,b=t.y;
        if(a==4&&b==4){print(t);return;}
        for(int i=0;i<4;i++){
            int xx=a+dx[i],yy=b+dy[i];
            if(xx<0||yy<0||xx>4||yy>4||vis[xx][yy]||maze[xx][yy]==1)continue;
            next.x=xx,next.y=yy,next.pre=front-1;
            q[end++]=next;
        }
    }
}
int main(){
    for(int i=0;i<5;i++)
    for(int j=0;j<5;j++){
        cin>>maze[i][j];
    }
    bfs();
    return 0;
}

 

https://vjudge.net/contest/283487#problem/D

搜索的本质是什么——就是往可能的方向发展,一旦搜索到了,就停止搜索

而广度搜索擅长处理走地图问题,从一点出发向各个方向延伸,直到达到目的地

重点是要有一个不同方向延伸的过程,这样的题才能用广度搜索

而深度搜索则是处理广度处理不了的问题

深度多用递归,就是对下一步按上一步的处理方法进行处理,看是否可行,与广度搜索本质相同

只是当使用方向延伸不能满足题目要求时,需要对问题具体情况具体分析时,就要用到深度搜索写一个单独的处理方法

#include<bits/stdc++.h>
using namespace std;
int x[11],ans[11];
int n,sum;
bool judge(int k){
    for(int i=1;i<k;i++)
        if(x[i]==x[k]||abs(i-k)==abs(x[i]-x[k]))return false;
    return true;
}
void dfs(int k){
    if(k>n){
        sum++;
        return;
    }
    for(int i=1;i<=n;i++){
        x[k]=i;
        if(judge(k))dfs(k+1);
    }
}
int main(){
    for(int i=1;i<=10;i++){
        sum=0;
        n=i;
        dfs(1);
        ans[i]=sum;
    }
    while(cin>>n&&n){
        cout<<ans[n]<<endl;
    }
    return 0;
}

 再者如果在优先搜索中遇到判断行走限制(不是指走出地图)或是有障碍的情况,就要编写judge函数

例如下面这道

https://vjudge.net/contest/283487#problem/F

#include<bits/stdc++.h>
using namespace std;
int p[5][5];
int high,num;
int n;
bool judge(int x,int y){
    for(int i=x;i>=1;i--){
        if(p[i][y]==1)return 0;
        if(p[i][y]==2)break;
    }
    for(int i=x;i<=n;i++){
        if(p[i][y]==1)return 0;
        if(p[i][y]==2)break;
    }
    for(int i=y;i>=1;i--){
        if(p[x][i]==1)return 0;
        if(p[x][i]==2)break;
    }
    for(int i=y;i<=n;i++){
        if(p[x][i]==1)return 0;
        if(p[x][i]==2)break;
    }
}
void dfs(){
    if(num>high)
        high=num;
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++){
        if(p[i][j]==0&&judge(i,j)){
            num++;
            p[i][j]=1;
            dfs();
            p[i][j]=0;
            num--;
        }
    }
}
int main(){
    //int n;
    char ch;
    while(cin>>n&&n){
        memset(p,0,sizeof(p));
        for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++){
            cin>>ch;
            if(ch=='X')p[i][j]=2;
        }
        high=num=0;
        dfs();
        cout<<high<<endl;
    }
    return 0;
}

 

posted @ 2019-03-04 22:49  helman78  阅读(123)  评论(0编辑  收藏  举报