• 寒假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;
    }
  • 相关阅读:
    香港两日游的那些事儿
    香港两日游的那些事儿
    Node.js下的Hello World
    Node.js下的Hello World
    Node.js下的Hello World
    WP SyntaxHighlighter 初探
    WP SyntaxHighlighter 初探
    WP SyntaxHighlighter 初探
    Google的代码高亮-code-prettify
    Java 18套JAVA企业级大型项目实战分布式架构高并发高可用微服务电商项目实战架构
  • 原文地址:https://www.cnblogs.com/helman/p/10465815.html
Copyright © 2020-2023  润新知