算法学习--Day10

今天开始了新一章的学习,前面的题目虽然做了几道,但是我觉得训练量仍然太小了。不过机试确实很多题目,并且难度也有所不同,所以要针对不同的题目进行专门的练习才好。题目类型有些多,等接下来我将搜索的题目写完后整体练习一下图论和搜索的题目,对其有个更好的理解之后再进行动态规划和贪心算法的题目。

 

今天记录一下广度搜索的内容,这里根据一道题目具体的去分析广度搜索的写法与思路。

 

Ignatius被魔王抓走了,有一天魔王出差去了,这可是Ignatius逃亡的好机会.

魔王住在一个城堡里,城堡是一个A*B*C的立方体,可以被表示成A个B*C的矩阵,刚开始Ignatius被关在(0,0,0)的位置,离开城堡的门在(A-1,B-1,C-1)的位置,现在知道魔王将在T分钟后回到城堡,Ignatius每分钟能从一个坐标走到相邻的六个坐标中的其中一个.现在给你城堡的地图,请你计算出Ignatius能否在魔王回来前离开城堡(只要走到出口就算离开城堡,如果走到出口的时候魔王刚好回来也算逃亡成功),如果可以请输出需要多少分钟才能离开,如果不能则输出-1.


输入数据的第一行是一个正整数K,表明测试数据的数量.每组测试数据的第一行是四个正整数A,B,C和T(1<=A,B,C<=50,1<=T<=1000),它们分别代表城堡的大小和魔王回来的时间.然后是A块输入数据(先是第0块,然后是第1块,第2块......),每块输入数据有B行,每行有C个正整数,代表迷宫的布局,其中0代表路,1代表墙.(如果对输入描述不清楚,可以参考Sample Input中的迷宫描述,它表示的就是上图中的迷宫)

特别注意:本题的测试数据非常大,请使用scanf输入,我不能保证使用cin能不超时.在本OJ上请使用Visual C++提交.


对于每组测试数据,如果Ignatius能够在魔王回来前离开城堡,那么请输出他最少需要多少分钟,否则输出-1.


Sample Input
1
3 3 4 20
0 1 1 1
0 0 1 1
0 1 1 1
1 1 1 1
1 0 0 1
0 1 1 1
0 0 0 0
0 1 1 0
0 1 1 0



Sample Output
11

 

这道题目就是很好的广度搜索题目,在此我将思路记录下来并方便以后查阅。

 

下面放上AC代码:


#include "stdio.h"
#include "iostream"
#include "queue"
using namespace std;
bool mark[50][50][50];
int maze[50][50][50];
struct N{
    int x,y,z;
    int t;
};
queue<N> Q;
int go[][3]={
        1,0,0,
        -1,0,0,
        0,1,0,
        0,-1,0,
        0,0,1,
        0,0,-1
};

int BFS(int x,int y,int z){
    while (!Q.empty()){
        N now = Q.front();
        Q.pop();
        for (int i = 0; i <6 ; ++i) {
            int nx = now.x + go[i][0];
            int ny = now.y + go[i][1];
            int nz = now.z + go[i][2];
            if(nx<0 || ny <0 || nz<0 ||nx>=x || ny>=y || nz>= z) continue;
            if(maze[nx][ny][nz]==1) continue;
            if(mark[nx][ny][nz]) continue;
            N tmp;
            tmp.x = nx;
            tmp.y = ny;
            tmp.z = nz;
            tmp.t = now.t +1 ;
            Q.push(tmp);
            mark[nx][ny][nz] = true;
            if(nx==x-1 && ny == y-1 && nz==z-1){  return tmp.t;}
        }
    }
    return -1;
}


int main(){
int n;
    cin>>n;
    while (n--){
        int x,y,z,t;
        cin>>x>>y>>z>>t;
        for (int i = 0; i < x; ++i) {
            for (int j = 0; j < y; ++j) {
                for (int k = 0; k < z; ++k) {
                    scanf("%d",&maze[i][j][k]);
                    mark[i][j][k] = false;
                }
            }
        }

        while(!Q.empty()) Q.pop();
        mark[0][0][0] = true;
        N tmp;
        tmp.x=tmp.y=tmp.z=tmp.t = 0;
        Q.push(tmp);
        int fin = BFS(x,y,z);
        if(fin<=t) cout<<fin<<endl;
        else cout<<-1<<endl;
    }
    return 0;
}

简单说明下代码,广度搜索的思想就是将起始点看做根,终点看做我们的一个树的叶子结点,只有到达了这个叶子结点我们才能得到解。

而广度搜索的意思就是一层一层遍历,每一层遍历结束后才可以进入下一层。

代码中,为了找到出口(x-1,y-y,z-1),我们要遍历每个点并且找到用步最少的方案。而每个父节点可以延伸最多6个子节点(其中会有重复的点),所以这个搜索空间是非常非常大的。所以此时我们就可以使用剪枝的思想,我们知道,迷宫中的每个点其实都是可以被多次访问的,但是并不是每次访问的方法都是最优解。但是我们知道,如果使用广度搜索第一次访问到的点所走的路径一定就会是离起始点最近的路径由上叙知识我们就可以得到,既然我们起点相同,那么中间的点为何不取最小的路径?所以我们就可以使用广度搜索,把每个点的最短路都找出来,并从当前最短路的点开始延伸。

代码中定义了队列Q,用来进行出对入队。走横向扫描。使用了三个continue来做剪枝

posted @ 2018-06-11 19:42  Ping_ing  阅读(189)  评论(0编辑  收藏  举报