• 搜索专题


    1.BFS

    1.1 BFS模板

    #include<cstdio>  
    #include<cstring>  
    #include<queue>  
    #include<algorithm>  
    using namespace std;  
    const int maxn=100;  
    bool  vst[maxn][maxn];  //访问标记  
    int dir[4][2]={0,1,0,-1,1,0,-1,0}; //方向向量,有些题目不需要  
    struct State //BFS 队列中的状态数据结构  
    {   
     int x,y; //坐标位置  
     int Step_Counter; //搜索步数统计器  
    };  
    State a[maxn];  
    /*   是否需要视题目而定
    bool CheckState(State s) //约束条件检验  
    {  
       if(!vst[s.x][s.y]&&...) //满足条件  
         return 1;  
       else    //约束条件冲突  
       return 0;  
    }  
    
    */
    void bfs(State st)  
    {  
      queue<State>q;  //BFS队列  
      State now,next;  //定义2个状态,当前和下一个  
      st.Step_Counter=0; //计数器清零  
      q.push(st);   //入队 ,,,,,,另一种不带参数的写法是将初始的状态进入队列
       vst[st.x][st.y]=1;  //访问标记  
      while(!q.empty())  
    {  
      now=q.front(); //取队首元素进行扩展  
      if(now==G) //出现目标态,此时为Step_Counter的最小值,可以退出即可  
      {  
            ......     //做相关处理  
          return;  
       }  
       for(int i=0;i<4;i++)  
       {  
           next.x=now.x+dir[i][0]; //按照规则生成下一个状态  
           next.y=now.y+dir[i][1];  
           next.Step_Counter=now.Step_Counter+1;  //计数器加1  
         if(CheckState(next))  //如果状态满足约束条件则入队  
         {  
           q.push(next);  
           vst[next.x][next.y]=1;   //访问标记  
          }  
    }  
       q.pop();  //队首元素出队  
    }  

    1.2 例题

    1.2.1 非常可乐
    Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
    Total Submission(s): 19932 Accepted Submission(s): 8063

    Problem Description
    大家一定觉的运动以后喝可乐是一件很惬意的事情,但是seeyou却不这么认为。因为每次当seeyou买了可乐以后,阿牛就要求和seeyou一起分享这一瓶可乐,而且一定要喝的和seeyou一样多。但seeyou的手中只有两个杯子,它们的容量分别是N 毫升和M 毫升 可乐的体积为S (S<101)毫升 (正好装满一瓶) ,它们三个之间可以相互倒可乐 (都是没有刻度的,且 S==N+M,101>S>0,N>0,M>0) 。聪明的ACMER你们说他们能平分吗?如果能请输出倒可乐的最少的次数,如果不能输出”NO”。

    Input
    三个整数 : S 可乐的体积 , N 和 M是两个杯子的容量,以”0 0 0”结束。

    Output
    如果能平分的话请输出最少要倒的次数,否则输出”NO”。

    Sample Input

    7 4 3
    4 1 3
    0 0 0

    Sample Output

    NO
    3

    Author

    seeyou

    解题代码

    #include<iostream>
    #include<queue>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    
    const int maxn=500;
    //方向向量,有些题不需要
    int dir[4][2]={0,1,0,-1,1,0,-1,0};
    //BFS队列中的状态数据结构
    int vis[maxn][maxn];
    struct State
    {
        int S,N,M;
        int step;
    };
    
    int s,n,m;
    int res=-1;//保存最终结果
    
    //约束条件检验
    //bool check(State s);
    queue<State>q;
    void bfs()
    {
    
        //定义两个状态,现在和下一个
        //将初始状态进入队列
        memset(vis,0,sizeof(vis));
        State now,next;
        q.push((State){s,0,0,0});
        vis[0][0]=1;
        while(!q.empty())
        {
            //取队首元素进行扩展
            now=q.front();
            //判断是否出现目标状态,此时为step最小值,可以退出即可
            if((now.S==s/2&&now.M==s/2)||(now.S==s/2&&now.N==s/2)||(now.M==s/2&&now.N==s/2))
            {
                res=now.step;
                //将剩余队列清空
                while(!q.empty())
                {
                    q.pop();
                }
                return;
            }
            q.pop();
            //开始进行搜索过程
            //本题主要是对瓶S,N,M有六种操作 S->N,S->M,N->S,N->M,M->S,M->N;
            //如果N瓶还未装满
            if(now.N<n)
            {
                //此过程又可以分为S可以装满N和S不可以装满N以及M可以装满N和M不可以装满N
                if(now.S>n-now.N&&!vis[n][now.M])
                {
                    vis[n][now.M]=1;
                    q.push((State){now.S-n+now.N,n,now.M,now.step+1});
                }
                if(now.S<n-now.N&&!vis[now.N+now.S][now.M])
                {
                    vis[now.S+now.N][now.M]=1;
                    q.push((State){0,now.S+now.N,now.M,now.step+1});
                }
                if(now.M>n-now.N&&!vis[n][now.M-n+now.N])
                {
                    vis[n][now.M-n+now.N]=1;
                    q.push((State){now.S,n,now.M-n+now.N,now.step+1});
                }
                if(now.M<n-now.N&&!vis[now.M+now.N][0])
                {
                    vis[now.M+now.N][0]=1;
                    q.push((State){now.S,now.M+now.N,0,now.step+1});
                }
    
            }
            //如果M瓶还没装满
            if(now.M<m)
            {
                 if(now.S>m-now.M&&!vis[now.N][m])
                {
                    vis[now.N][m]=1;
                    q.push((State){now.S-m+now.M,now.N,m,now.step+1});
                }
               if(now.S<m-now.M&&!vis[now.N][now.S+now.M])
                {
                    vis[now.N][now.S+now.M]=1;
                    q.push((State){0,now.N,now.S+now.M,now.step+1});
                }
                if(now.N>m-now.M&&!vis[now.N-m+now.M][m])
                {
                    vis[now.N-m+now.M][m]=1;
                    q.push((State){now.S,now.N-m+now.M,m,now.step+1});
                }
                if(now.N<m-now.M&&!vis[0][now.M+now.N])
                {
                    vis[0][now.M+now.N]=1;
                    q.push((State){now.S,0,now.M+now.N,now.step+1});
                }
            }
            //对于S瓶而言,其他瓶不可能把S装满
            if(now.S<s)
            {
                if(now.N<s-now.S&&!vis[0][now.M])
                {
                    vis[0][now.M]=1;
                    q.push((State){now.N+now.S,0,now.M,now.step+1});
                }
                if(now.M<s-now.S&&!vis[now.N][0])
                {
                    vis[now.N][0]=1;
                    q.push((State){now.S+now.M,now.N,0,now.step+1});
                }
            }
        }
    }
    int main()
    {
        while(~scanf("%d%d%d",&s,&n,&m))
        {
            res=-1;
            if(s==0&&n==0&&m==0)
                break;
            if(s%2!=0)
            {
                 printf("NO
    ");
                 continue;
            }
            bfs();
           if(res==-1)
           {
               printf("NO
    ");
               continue;
           }
           else
           {
               printf("%d
    ",res);
           }
    
    
        }
        return 0;
    }
    
    

    1.2.2胜利大逃亡(续)
    Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
    Total Submission(s): 9963 Accepted Submission(s): 3593

    Problem Description
    Ignatius再次被魔王抓走了(搞不懂他咋这么讨魔王喜欢)……

    这次魔王汲取了上次的教训,把Ignatius关在一个n*m的地牢里,并在地牢的某些地方安装了带锁的门,钥匙藏在地牢另外的某些地方。刚开始Ignatius被关在(sx,sy)的位置,离开地牢的门在(ex,ey)的位置。Ignatius每分钟只能从一个坐标走到相邻四个坐标中的其中一个。魔王每t分钟回地牢视察一次,若发现Ignatius不在原位置便把他拎回去。经过若干次的尝试,Ignatius已画出整个地牢的地图。现在请你帮他计算能否再次成功逃亡。只要在魔王下次视察之前走到出口就算离开地牢,如果魔王回来的时候刚好走到出口或还未到出口都算逃亡失败。

    Input

    每组测试数据的第一行有三个整数n,m,t(2<=n,m<=20,t>0)。接下来的n行m列为地牢的地图,其中包括:

    . 代表路
    * 代表墙 @ 代表Ignatius的起始位置 ^ 代表地牢的出口 A-J 代表带锁的门,对应的钥匙分别为a-j a-j 代表钥匙,对应的门分别为A-J

    每组测试数据之间有一个空行。

    Output

    针对每组测试数据,如果可以成功逃亡,请输出需要多少分钟才能离开,如果不能则输出-1。

    Sample Input

    4 5 17
    @A.B.
    a*.*.
    *..*^
    c..b*
    
    4 5 16
    @A.B.
    a*.*.
    *..*^
    c..b*

    Sample Output

    16
    -1

    思路:
    BFS+状态压缩
    对钥匙状态压缩,因为有10个钥匙,所以钥匙串为10位的2进制数串。
    在搜索过程中会碰到3种有效情况,碰到路径,碰到钥匙,碰到门;

    • 碰到路径,进行正常的标记操作
    • 碰到钥匙,对钥匙串进行或运算,把碰到的钥匙加到钥匙串
    • 碰到门,进行与运算,查看是否有相应的钥匙
    #include<iostream>
    #include<string.h>
    #include<stdio.h>
    #include<queue>
    using namespace std;
    const int maxn=25;
    char ma[maxn][maxn];
    
    bool vis[maxn][maxn][1025];//10种钥匙的状态
    int m,n,t;
    int sx,sy;
    struct State
    {
        int x,y;
        int step,key;
    };
    
    
    int dir[4][2]={1,0,-1,0,0,1,0,-1};
    
    
    bool check(int x,int y)
    {
        if(x>=0&&x<n&&y>=0&&y<m&&ma[x][y]!='*')
            return true;
        else return false;
    }
    
    
    int bfs()
    {
        int k;
        memset(vis,false,sizeof(vis) );
        queue<State>q;
        State now,next;
        now.x=sx;
        now.y=sy;
        now.key=0;
        now.step=0;
    
    
        q.push(now);
        vis[sx][sy][0]=1;
        while(!q.empty())
        {
            now=q.front();
            //出现目标状态
            q.pop();
            if(ma[now.x][now.y]=='^')
            {
                if(now.step<t)
                    return now.step;
            }
            if(now.step>=t) break;
            for(int i=0;i<4;i++)
            {
                next.x=now.x+dir[i][0];
                next.y=now.y+dir[i][1];
                next.step=now.step+1;
                next.key=now.key;
                if(check(next.x,next.y))
                {
                    //碰到三种情况 碰到钥匙 碰到门 碰到路径
                    //碰到门的情况,钥匙与门进行与操作
                    if(ma[next.x][next.y]<='J'&&ma[next.x][next.y]>='A')
                    {
                        k=ma[next.x][next.y]-'A';
                        if((next.key>>k)&1&&!vis[next.x][next.y][next.key])
                        {
                            vis[next.x][next.y][next.key]=true;
                            q.push(next);
                        }
                    }
                    //碰到钥匙的情况,采用或运算放入钥匙
                    else if(ma[next.x][next.y]<='j'&&ma[next.x][next.y]>='a')
                    {
                        k=1<<(ma[next.x][next.y]-'a');
                        next.key|=k;
                        if(!vis[next.x][next.y][next.key])
                        {
                            vis[next.x][next.y][next.key]=true;
                            q.push(next);
                        }
                    }
                    else
                    {
                        if(!vis[next.x][next.y][next.key])
                        {
                            vis[next.x][next.y][next.key]=true;
                            q.push(next);
                        }
                    }
                }
            }
        }
        return -1;
    
    }
    
    
    int main()
    {
        int i,j;
        freopen("test.txt","r",stdin);
        while(scanf("%d%d%d",&n,&m,&t)!=EOF)
        {
            for(int i=0;i<n;i++)
            {
                scanf("%s",ma[i]);
                for(j=0;j<m;j++)
                {
                    if(ma[i][j]=='@')
                    {
                        sx=i;
                        sy=j;
                    }
                }
            }
            printf("%d
    ",bfs());
        }
    
        return 0;
    }
    

    2. DFS

    2.1 DFS模板

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    using namespace std;
    const int maxn=100;
    bool vst[maxn][maxn];   //访问标记  
    // bool vis[maxn];     //一维状态标记
    int map[maxn][maxn];  //坐标范围
    int dir[4][2]={0,1,0,-1,1,0,-1,0};  //方向向量,(x,y)周围的四个方向
    bool CheckEdge(intx,int y)    //边界条件和约束条件的判断
    {
       if(!vst[x][y]&&...)  //满足条件
          return 1;
       else     // 与约束条件冲突
         return  0;
    }
    
    void dfs(int x,int y)
    {
        vst[x][y]=1; //标记该节点被访问过
    
          if(map[x][y]==G)  //出现目标态G
          {
               ......  //做相应处理
    
           return;
          }
         for(int i=0;i<4;i++)
        {
          if(CheckEdge(x+dir[i][0],y+dir[i][1])) //按照规则生成下一个节点
           dfs(x+dir[i][0],y+dir[i][1]);
         }
         return;//没有下层搜索节点,回溯
    }
    int main()
    {
       ......
      return 0;
    }
    

    2.2 DFS 例题

    2.2.1 棋盘问题
    Time Limit: 1000MS Memory Limit: 10000K
    Total Submissions: 58295 Accepted: 28027
    Description

    在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别。要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子的所有可行的摆放方案C。
    Input

    输入含有多组测试数据。
    每组数据的第一行是两个正整数,n k,用一个空格隔开,表示了将在一个n*n的矩阵内描述棋盘,以及摆放棋子的数目。 n <= 8 , k <= n
    当为-1 -1时表示输入结束。
    随后的n行描述了棋盘的形状:每行有n个字符,其中 # 表示棋盘区域, . 表示空白区域(数据保证不出现多余的空白行或者空白列)。
    Output

    对于每一组数据,给出一行输出,输出摆放的方案数目C (数据保证C<2^31)。
    Sample Input

    2 1
    #.
    .#
    4 4
    ...#
    ..#.
    .#..
    #...
    -1 -1

    Sample Output

    2
    1

    解题代码:

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    using namespace std;
    
    
    const int maxn=500;
    //设置访问标记
    //bool vis[maxn][maxn];
    bool vis[maxn];//每一列棋子的访问标志
    char m[maxn][maxn];
    //设置方向向量
    int dir[4][2]={1,0,-1,0,0,1,0,-1};
    //已放棋子的数目
    int cnt;
    //放置棋子的方案数
    int sum;
    int k,n;
    //边界约束条件
    /*
    bool CheckEdge(int x,int y)
    {
        if(!vis[x][y]&&.....) //满足条件
            return 1;
        else                   //与约束条件冲突
            return 0;
    }
    
    */
    void dfs(int s)
    {
    
        //检测是否满足条件
        if(cnt==k)
        {
            sum++;
            return;
        }
        else
        {
            //判断是否越界
            if(s>=n)return;
            else
            {
                //开始搜索过程
                //首先尝试将棋子放在0-n-1的某一列
                for(int i=0;i<n;i++)
                {
                    //如果当前列有棋盘而且没有被标记
                    if(m[s][i]=='#'&&!vis[i])
                    {
                        vis[i]=1;
                        cnt++;
                        //继续回溯搜索下一步
                        dfs(s+1);
                        //经过一轮搜索没结果则回溯
                        cnt--;
                        vis[i]=0;
    
                    }
    
                }
                dfs(s+1);
            }
        }
    }
    int main()
    {
        while(cin>>n>>k)
        {
            memset(vis,0,sizeof(vis));
            sum=cnt=0;
            if(n==-1&&k==-1)
                break;
            for(int i=0;i<n;i++)
                for(int j=0;j<n;j++)
                    cin>>m[i][j];
            dfs(0);
            cout<<sum<<endl;
        }
    }
    
  • 相关阅读:
    proxy
    Reflect
    五一假期总结
    寄生组合式继承
    promise A+ 第二遍
    promise A+ 规范 第三遍
    类型转化
    作用域和闭包
    手写 instanceof 第二遍
    vue eltree 单选实现
  • 原文地址:https://www.cnblogs.com/bryce1010/p/9386806.html
Copyright © 2020-2023  润新知