zoukankan      html  css  js  c++  java
  • 搜索专题

    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;
        }
    }
    
  • 相关阅读:
    开放源码的对象关系映射工具ORM.NET 插入数据 Insert/Update Data
    开放源码的对象关系映射工具ORM.NET 快档开发入门 Quick Start
    .NET 动态脚本语言Script.NET 开发指南
    开放源码的对象关系映射工具ORM.NET 删除数据 Deleting Records using ORM.NET
    .NET Remoting过时了吗?为什么公司的项目还是选择用.NET Remoting,而不是WCF?
    开放源码的对象关系映射工具ORM.NET 查看和显示数据 View and Display data using ORM.NET
    开放源码的对象关系映射工具ORM.NET 查询表 调用存储过程 增加自定义代码
    技术人生:坚持,每日一博
    CQRS:CQRS + DDD + MDP 实现快速应用程序开发
    NodeJs:Happy代码生成器,重构了代码,更新了文档,完善了示例,欢迎下载使用
  • 原文地址:https://www.cnblogs.com/bryce1010/p/9386806.html
Copyright © 2011-2022 走看看