• 寒假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
    ", 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)
    ",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;
    }
  • 相关阅读:
    淘淘整理相关链接
    Ajax跨域请求
    tomcat启动(六)Catalina分析-StandardServer.start()
    Tomcat中组件的生命周期管理公共接口Lifecycle
    Catalina.createDigester方法详细理解
    SpringMVC访问WEB-INF下的jsp的方法
    tomcat启动(五)Catalina分析-service.init
    tomcat启动(四)Catalina分析-server的init()方法
    tomcat启动(三)Catalina分析-load方法分析
    tomcat启动(三)Catalina简要分析
  • 原文地址:https://www.cnblogs.com/helman/p/10465815.html
Copyright © 2020-2023  润新知