zoukankan      html  css  js  c++  java
  • 7.23 深搜广搜

    深搜(DFS)  关键词:回溯

    栈实现,(递归本质和栈一样)一直走到底再回溯,时间复杂度高,空间低

    #include<iostream>
    #include<cstring>
    using namespace std;
    int R,C;
    char maps[40][40];
    int dp[40][40];
    int dir[2][4]={{1,0,0,-1},{0,1,-1,0}};
    int ans=1<<30;
    void DFS(int x,int y,int step){
          if(x>=R || y>=C || x<0 ||y<0) return ;
          if(x==R-1&&y==C-1) {
            if(step<ans) ans=step;
            return ;
          }
          if(maps[x][y]=='#') return;
          for(int i=0;i<4;i++){
                if(x+dir[0][i] >=R || y+dir[1][i] >=C || x+dir[0][i] <0 || y+dir[1][i] < 0 ) continue;
                if(maps[x+dir[0][i]][y+dir[1][i]]=='.' && !dp[x+dir[0][i]][y+dir[1][i]]){
                     dp[x+dir[0][i]][y+dir[1][i]]=1;
                     DFS(x+dir[0][i],y+dir[1][i],step+1);//也是一直走一直走,直到回溯的时候才置0;
                     dp[x+dir[0][i]][y+dir[1][i]]=0;
                }
          }
    }
    int main(){
        cin>>R>>C;
        char c;
        for(int i=0;i<R;i++)
        for(int j=0;j<C;j++){
            cin>>c;
            maps[i][j]=c;
            dp[i][j]=0;
        }
        dp[0][0]=1;
        DFS(0,0,1);
        cout<<ans<<endl;
     return 0;
     }
    View Code

    广搜(BFS) 关键词:最优解  结构体  状态保存

    第一次找到的就是最优解(最少步数,最少转弯次数),层次结构,一层一层地搜索,无回溯过程,队列实现,结构体记录状态,入队。

    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<queue>
    using namespace std;
    int R,C;
    char maps[40][40];
    int dp[40][40];
    int dir[2][4]={{1,0,0,-1},{0,1,-1,0}};
    int ans=1<<30;
    struct node{
        int x;
        int y;
        int step;
        node(int xx,int yy,int tt):x(xx),y(yy),step(tt){}
    };
    int  bfs()
    {
         queue<node> que;
         que.push(node(0,0,1));
         while(!que.empty()){
            node temp = que.front();
            que.pop();
            for(int i=0;i<4;i++){     //上下左右四方向
                int tx=temp.x+dir[0][i];
                int ty=temp.y+dir[1][i];
                int tt=temp.step;
                if(dp[tx][ty]) continue;
                if(tx<0 || tx>=R  || ty<0  || ty>=C ) continue;
                if(maps[tx][ty]=='#') continue;
                if(tx==R-1&&ty==C-1) return tt+1;//直接return,因为最先找到的一定是最优解
                dp[tx][ty]=1;
                que.push(node(tx,ty,tt+1));
            }
        }
    }
    int main(){
        cin>>R>>C;
        for(int i=0;i<R;i++)
        for(int j=0;j<C;j++){
            cin>>maps[i][j];
            dp[i][j]=0;
        }
        int ans=bfs();
        cout<<ans<<endl;
        return 0;
    View Code

    A题:

    AveryBoy最近迷上了连连看游戏,于是他自己写了一个程序来玩,不过由于他学艺不精导致他写的连连看游戏连线不能从外面绕过。

    游戏规则:在一个棋盘中,放了很多的棋子。如果某两个相同的棋子,可以通过一条线连起来(这条线不能经过其它棋子),而且线的转折次数不超过两次,那么这两个棋子就可以在棋盘上消去。玩家鼠标先后点击两块棋子,试图将他们消去,然后游戏的后台判断这两个方格能不能消去。现在你的任务就是写这个后台程序。

    Input

    输入数据有多组。每组数据的第一行有两个正整数n,m(0<n<=1000,0<m<1000),分别表示棋盘的行数与列数。

    在接下来的n行中,每行有m个非负整数描述棋盘的方格分布。0表示这个位置没有棋子,正整数表示棋子的类型。接下来的一行是一个正整数q(0<q<50),表示下面有q次询问。在接下来的q行里,每行有四个正整数x1,y1,x2,y2,表示询问第x1行y1列的棋子与第x2行y2列的棋子能不能消去。n=0,m=0时,输入结束。

    Output

    每一组输入数据对应一行输出。如果能消去则输出"YES",不能则输出"NO"。

    Sample Input

    3 4
    1 2 3 4
    0 0 0 0
    4 3 2 1
    4
    1 1 3 4
    1 1 2 4
    1 1 3 3
    2 1 2 4
    3 4
    0 1 4 3
    0 2 4 1
    0 0 0 0
    2
    1 1 2 4
    1 3 2 3
    0 0
    

    Sample Output

    YES
    NO
    NO
    NO
    NO
    YES
    

    这题就是迷宫转弯问题,转弯次数小于等于2即可
    #include <bits/stdc++.h>
    using namespace std;
    int n,m;
    int x2,y2;
    int maze[1010][1010];
    int used[1010][1010];
    int nx[4]={1,0,-1,0},ny[4]={0,1,0,-1};
    struct node
    {
        int x,y;
    };
    bool canmove(int x,int y)
    {
        if(x>=0&&x<n&&y>=0&&y<m&&maze[x][y]==0)return true;
        else if(x>=0&&x<n&&y>=0&&y<m&&maze[x][y]==maze[x2][y2])return true;
        else return false;
    }
    void bfs(int x,int y)
    {
        queue<node> que;
        node now,next;
        now.x=x;
        now.y=y;
        que.push(now);
        bool flag=false;
        while(!que.empty())
        {
            now=que.front();
            que.pop();
            if(now.x==x2&&now.y==y2)
            {
                if(used[x2][y2]<=3)
                {
                    flag=true;
                    printf("YES
    ");
                }
                else break;
            }
            else
            {
                for(int i=0;i<4;i++)
                {
                    next.x=now.x+nx[i];
                    next.y=now.y+ny[i];
                    while(canmove(next.x,next.y))
                    {
                        if(used[next.x][next.y]==0)
                        {
                            used[next.x][next.y]=used[now.x][now.y]+1;
                            que.push(next);
                        }
                        next.x+=nx[i];
                        next.y+=ny[i];
                    }
                }
            }
        }
        if(flag==false)printf("NO
    ");
    }
    int main()
    {
        while(1)
        {
            scanf("%d%d",&n,&m);
            if(n==0&&m==0)break;
            int i,j;
            for(i=0;i<n;i++)
            {
                for(j=0;j<m;j++)scanf("%d",&maze[i][j]);
            }
            int q;
            scanf("%d",&q);
            for(i=0;i<q;i++)
            {
                int x1,y1;
                scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
                x1--,x2--,y1--,y2--;
                if(maze[x1][y1]!=maze[x2][y2])printf("NO
    ");
                else if(maze[x1][y1]==0||maze[x2][y2]==0)printf("NO
    ");
                else
                {
                    bfs(x1,y1);
                    memset(used,0,sizeof used);
                }
            }
        }
        return 0;
    }
    View Code

    B题:

    经典的迷宫转弯问题

    AveryBoy被困在一个n行m列的迷宫中了,问他能不能从起点到终点。迷宫中字符'.'表示该位置为空地,字符'*'表示该位置为障碍,他必须绕行,不过如果他转太多弯就会晕倒。假定给定的两个位置都是空地,初始时,AveryBoy所面向的方向未定,她可以选择4个方向的任何一个出发,而不算成一次转弯。

    Input

    第1行为一个整数t (1 ≤ t ≤ 100),表示测试数据的个数,接下来为t组测试数据,每组测试数据中,

    第1行为两个整数n, m (1 ≤ m, n ≤ 100),分别表示迷宫的行数和列数,接下来n行,每行包括m个字符。

    每组测试数据的最后一行为5个整数k, x1, y1, x2, y2 (1 ≤ k ≤ 10, 1 ≤ x1, x2 ≤ n, 1 ≤ y1, y2 ≤ m),其中k表示AveryBoy最多能转的弯数,(x1, y1), (x2, y2)表示起点和终点。

    Output

    每组测试数据对应为一行,若AveryBoy能从起点到终点,输出“yes”,否则输出“no”。

    Sample Input

    2
    5 5
    ...**
    *.**.
    .....
    .....
    *....
    1 1 1 3 1
    5 5
    ...**
    *.**.
    .....
    .....
    *....
    2 1 1 3 1
    

    Sample Output

    no
    yes
    

    很经典的转弯问题
    因为要看能不能过迷宫,利用广搜可以求出最少的转弯次数,如果最少转弯次数<=k,那么就可以通过迷宫
    特别的是,因为起始方向不确定,所以最开始的转弯不计做一次转弯
    开一个used[][]数组,记录当前点的转弯次数
    先将used[][]全置为-1,(其实全置0也一样,最后比较的时候减去1就行)
    定义now,next节点,对于每一个now节点,遍历它的四个方向
    关键:找到一个方向,就一直把该方向走完,把这个方向上的所有未被标记过的节点的usd值全部标记,等于上一次转弯节点(now)的used值+1;
    这里的判重即是判断used[x][y]是否等于-1,若已经标记过,那么我们跳过它,因为有的节点是交叉点

     

    思路:对队列里面的坐标(x,y)做选择时,每次选择一个方向(共四个方向)走到不能走为止,将沿途经过的所有used值 = -1的点入队并更新used值(对used不为-1的点不作处理)。队列为空还无法到终点,输出no。若能遍历到终点,判断used值是否大于要求的k,若大于输出no,否则输出yes。

     
    #include <bits/stdc++.h>
    using namespace std;
    int used[110][110];
    int n,m;
    int k,x2,y2;
    char maze[110][110];
    int nx[4]={1,0,-1,0},ny[4]={0,1,0,-1};
    struct node
    {
        int x,y;
    };
    bool canmove(int x,int y)
    {
        if(x>=0&&x<n&&y>=0&&y<m&&maze[x][y]=='.')return true;
        else return false;
    }
    void bfs(int x,int y)
    {
        queue<node> que;
        node now,next;
        now.x=x;
        now.y=y;
        que.push(now);
        bool flag=false;
        while(!que.empty())
        {
            now=que.front();
            que.pop();
            if(now.x==x2&&now.y==y2)
            {
                if(used[x2][y2]<=k)
                {
                    flag=true;
                    printf("yes
    ");
                }
                else break;
            }
            else
            {
                for(int i=0;i<4;i++)
                {
                    next.x=now.x+nx[i];
                    next.y=now.y+ny[i];
                    while(canmove(next.x,next.y))
                    {
                        if(used[next.x][next.y]==-1)
                        {
                            used[next.x][next.y]=used[now.x][now.y]+1;
                            que.push(next);
                        }
                        next.x+=nx[i];
                        next.y+=ny[i];
                    }
                }
            }
        }
        if(flag==false)printf("no
    ");
    }
    int main()
    {
        int t;
        cin>>t;
        while(t--)
        {
            int i,j;
            scanf("%d%d",&n,&m);
            for(i=0;i<n;i++)
            {
                scanf("%s",maze[i]);
            }
            int x1,y1;
            scanf("%d%d%d%d%d",&k,&x1,&y1,&x2,&y2);
            x1--,y1--,x2--,y2--;
             for(i=0;i<n;i++)
            {
                for(j=0;j<m;j++)used[i][j]=-1;
            }
            bfs(x1,y1);
        }
        return 0;
    }
    View Code

    C题:

    这次AveryBoy被困在一个三维迷宫中,他必须想办法在T分钟内离开迷宫(包括T)。迷宫是一个A*B*C的立方体,起点在(0,0,0)位置,终点在(A-1,B-1,C-1)位置。如果他能离开迷宫,输出离开迷宫所需最短时间,否则输出-1。

    三维迷宫问题,仍然是找最优解,求最小步数(时间)在二维基础上修改结构体内x,y为x,y,z,修改方向为x,y,z方向上的六个方向即可,稍加变通,不要被吓到。

    #include <bits/stdc++.h>
    using namespace std;
    int maze[55][55][55];
    int nx[6]={1,-1,0,0,0,0};
    int ny[6]={0,0,1,-1,0,0};
    int nz[6]={0,0,0,0,1,-1};
    int dp[55][55][55];
    int X,Y,Z;
    struct node
    {
        int x,y,z,step;
        node(int xx,int yy,int zz,int ss):x(xx),y(yy),z(zz),step(ss){}
    };
    int  bfs()
    {
         queue<node> que;
         que.push(node(0,0,0,0));
         bool flag=false;
         while(!que.empty())
        {
            node temp = que.front();
            que.pop();
            for(int i=0;i<6;i++)
                {     //上下左右四方向
                int tx=temp.x+nx[i];
                int ty=temp.y+ny[i];
                int tz=temp.z+nz[i];
                int tt=temp.step;
                if(dp[tx][ty][tz]) continue;
                if(tx<0 || tx>=X  || ty<0  || ty>=Y||tz<0||tz>=Z ) continue;
                if(maze[tx][ty][tz]==1) continue;
                if(tx==X-1&&ty==Y-1&&tz==Z-1)
                {
                    flag=true;
                    return tt+1;//直接return,因为最先找到的一定是最优解
                }
                dp[tx][ty][tz]=1;
                que.push(node(tx,ty,tz,tt+1));
            }
        }
        if(flag==false)return -1;
    }
    int main()
    {
        int t;
        cin>>t;
        while(t--)
        {
            int tt;
            scanf("%d%d%d%d",&X,&Y,&Z,&tt);
            int i,j,k;
            for(i=0;i<X;i++)
            {
                for(j=0;j<Y;j++)
                {
                    for(k=0;k<Z;k++)
                    {
                        scanf("%d",&maze[i][j][k]);
                    }
                }
            }
            memset(dp,0,sizeof dp);
            int ans=bfs();
            if(ans<=tt&&ans!=-1)printf("%d
    ",ans);
            else printf("-1
    ");
        }
        return 0;
    }
    View Code

    D题:

    如下图的九宫格中,放着 1~8 的数字卡片,还有一个格子空着。与空格子相邻的格子中的卡片可以移动到空格中。经过若干次移动,可以形成第二个图所示的局面。


    我们把第一个图的局面记为:12345678.
    把第二个图的局面记为:123.46758
    显然是按从上到下,从左到右的顺序记录数字,空格记为句点。
    本题目的任务是已知九宫的初态和终态,求最少经过多少步的移动可以到达。如果无论多少步都无法到达,则输出-1。

    经典的八数码问题,依然找最优解,用字符串记状态,结构体数组内放入现在数码块的状态char sz[3][3],开一个map<string,int>记录该状态是否已出现过,用x,y记录0的位置

    剪枝,奇排列永远不能变成偶排列

    #include<bits/stdc++.h>
    using namespace std;
    char s1[10],f1[10];
    bool nixu()
    {
        int ans1=0;
        int ans2=0;
        int i,j;
        string ss1="",ff1="";
        for(i=0;i<9;i++)
        {
            if(s1[i]!='.')ss1+=s1[i];
            if(f1[i]!='.')ff1+=f1[i];
        }
        //cout<<ss1<<endl;
        //cout<<ff1<<endl;
        for(i=1;i<8;i++)
        {
            for(j=0;j<8-i;j++)
            {
                if(ss1[j]>ss1[j+1])
                {
                    char t=ss1[j];
                    ss1[j]=ss1[j+1];
                    ss1[j+1]=t;
                    ans1++;
                }
            }
        }
         for(i=1;i<8;i++)
        {
            for(j=0;j<8-i;j++)
            {
                if(ff1[j]>ff1[j+1])
                {
                    char t=ff1[j];
                    ff1[j]=ff1[j+1];
                    ff1[j+1]=t;
                    ans2++;
                }
            }
        }
        //cout<<ans1<<' '<<ans2;
        if(ans1%2!=ans2%2)return false;
        else return true;
    }
    struct node
    {
        int x,y,step;  //x,y记下0(.)的位置
        char sz[3][3];
    };
    char start[10],finally[10];
    string temp;
    map<string,bool> mp;//代表状态有无被标记  字符串代表状态
    int inf=999999999;
    int nx[4]={1,0,0,-1},ny[4]={0,1,-1,0};
    int bfs()
    {
        int ans=inf;
        node now,next;
        int d=0,i,j;
        for(i=0;i<3;i++)
        {
            for(j=0;j<3;j++)
            {
                now.sz[i][j]=start[d++];
                if(now.sz[i][j]=='.')
                {
                    now.x=i;
                    now.y=j;
                }
            }
        }
        now.step=0;
        queue<node> que;
        que.push(now);
        while(!que.empty())
        {
            now=que.front();
            que.pop();
            for(i=0;i<4;i++)
            {
                next=now;    //让这两个状态下的字符数组都相同
                next.x=now.x+nx[i];
                next.y=now.y+ny[i];
                if(next.x<0||next.x>2||next.y<0||next.y>2)continue;
                next.step=now.step+1;
                next.sz[now.x][now.y]=next.sz[next.x][next.y];//now.x,now.y代表的就是先前状态下的0的位置
                next.sz[next.x][next.y]='.';//与0交换
                temp="";
                for(j=0;j<3;j++)
                {
                    for(int k=0;k<3;k++)
                        temp+=next.sz[j][k];
                }
                if(temp==finally)
                {
                    ans=min(ans,next.step);
                }
                if(!mp[temp])
                {
                    mp[temp]=true;
                    que.push(next);
                }
            }
        }
        return ans;
    }
    int main()
    {
        int t;
        cin>>t;
        while(t--)
           {
               scanf("%s%s",start,finally);
               strcpy(s1,start);
            strcpy(f1,finally);
            if(!nixu())printf("-1
    ");
            else
            {
                int ans=bfs();
                //cout<<ans<<endl;
                if(ans==inf)printf("-1
    ");
                else printf("%d
    ",ans);
            }
           }
        return 0;
    }
    View Code
    
    
  • 相关阅读:
    hdu 1104 数论+bfs
    hdu 1019 最小公倍数
    hdu 1005 数论 循环
    山东省第三届acm
    hdu 1576
    浏览器支持
    FormData
    获取APP图片资源
    链接转标签
    bug20170125
  • 原文地址:https://www.cnblogs.com/raincle/p/9368312.html
Copyright © 2011-2022 走看看