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\n");
            }
            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\n");
}
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\n");
            else if(maze[x1][y1]==0||maze[x2][y2]==0)printf("NO\n");
            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\n");
            }
            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\n");
}
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\n",ans);
        else printf("-1\n");
    }
    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\n");
        else
        {
            int ans=bfs();
            //cout<<ans<<endl;
            if(ans==inf)printf("-1\n");
            else printf("%d\n",ans);
        }
       }
    return 0;
}
View Code

 

 


posted @ 2018-07-25 20:56  raincle  阅读(303)  评论(0编辑  收藏  举报