题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1072
题目大意:
就是给你一个n*m的地图,地图的数字
0=墙 1=可以走的平地 2=起点 3=终点 4=炸弹时间重置点
细节性的东西:到达终点或者是炸弹时间重置点时,你限定的时间为最后一秒,则这样的路径是不成立的,即这样的路径不合题意。
bfs思路
bfs思路:就是发散。通过一个点发散到多个点,然后再由多个点再发散到另外多个点。下面的代码中有一句话,就是:map[sx][sy]=0; //这句话去掉与否都不影响答案
最开始不加上这句话是也是可以过得。我想了一下有没有可能起点会经过两次呢?就像那个测试案例三一样。测试案例三:这个走的路径数是13为(2->1->1->1->4->1->1->1->1->4->1->1->1->3)第一个4的左右所经过的1就是同一个1.。
5 8
1 2 1 1 1 1 1 4
1 0 0 0 1 0 0 1
1 4 1 0 1 1 0 1
1 0 0 0 0 3 0 1
1 1 4 1 1 1 1 1所以我在想有没有可能出现这种情况:(就是起点2要经过4然后再出来,就像下面的图一样。然后我发现就是2不经过4到终点3的步数是4,而2到4再出来到3的步数是5。所以基本上可以认为不存在起点经过两次的情况。所以map[sx][sy]=0; 可以设置。当然删掉这句话也是可以ac的,时间复杂度是符合要求的)
3 6
0 2 1 1 1 3
0 4 0 0 0 0
bfs的ac代码:
#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
struct node{
int x;
int y;
int step;
int time;
};
int n,m;
int dx[4]={0,1,0,-1};//左下右上
int dy[4]={-1,0,1,0};
int map[10][10];
int sx,sy;
void bfs(){
queue<node> Q;
node q;
q.x = sx;
q.y = sy;
q.step = 0;
q.time = 6;
Q.push(q);
map[sx][sy]=0; //这句话去掉与否都不影响答案
while(!Q.empty()){
node p;//用来接收前一个的数据
p = Q.front();
Q.pop();
for(int i = 0;i < 4;i++){//进行发散查找
node k;
k.x = p.x+dx[i];
k.y = p.y+dy[i];
k.step = p.step+1;
k.time = p.time-1;
if(k.x<1||k.x>n||k.y<1||k.y>m||map[k.x][k.y]==0||k.time<=0){
continue;
}
if(map[k.x][k.y]==3){
cout<<k.step<<endl;
return;
}
if(map[k.x][k.y]==4){//如果是碰到重置点就
//时间重置
k.time = 6;
map[k.x][k.y] = 0;
}
Q.push(k);
}
}
cout<<-1<<endl;
}
int main(){
int t;
cin>>t;
while(t--){
cin>>n>>m;
for(int i = 1;i <= n;i++){
for(int j = 1;j <= m;j++){
cin>>map[i][j];
if(map[i][j] == 2){
sx = i;
sy = j;
}
}
}
bfs();
}
return 0;
}
dfs思路
说一下dfs思路吧,就是正常地左下右上地搜索。然后需要step[10][10]和time[10][10]的两个数组,分别记录每个点的走过的步数以及在这个点所剩的时间。
然后这里需要用到剪掉一些用不到的路径,剪的方法如下:
就是step数组初始值设置个无穷大的值
然后遍历的时候,当sum>=step[x][y]&&T<=time[x][y]时就将这个路径剪掉,即ruturn掉。
原因是:sum代表当前点的步数,T代表当前点的所剩时间。而在step[x][y]和time[x][y]数值还没有更新的时候,sum大于step[x][y]证明,之前有路径走过这个(x,y)点【注:因为step初始值是设置成类似无穷大的值的啦】。然后既然证明有(x,y)被走过,那么也就是所time[x][y]也是代表之前那个路径走过所留下的time值。
所以当前的步数大于或者等于之前路径的步数,且时间小于等于之前路径的时间,那就不用走了。最后可定是走不到终点的。【故剪掉】
dfs的ac代码:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int map[10][10];
int n,m;//迷宫的行和列
int step[10][10];//记录当前之前所走的步数
int time[10][10];//记录每步的时间
int dx[4]={0,1,0,-1};
int dy[4]={-1,0,1,0};
int sx,sy;
int ex,ey;
int minstep;//用来记录最短的时间
//int maxtime;//最大的限定时间
int dfs(int x,int y,int sum,int T){
// cout<<x<<"|"<<y<<"|"<<sum<<"|"<<T<<endl;
if(x == ex && y == ey){//到达终点时
if(minstep == -1||minstep > sum){
minstep = sum;
}
}
if(T <= 0){//当时间等于maxtime的时候就返回
return 0;
}
if(sum >= step[x][y] && T <= time[x][y]){//当走到重复的路径时且当前的时间小于等于之前的time就直接结束
return 0;
}
if(map[x][y] == 4){//当遇见炸弹重置点将时间重置为6
T = 6;
}
step[x][y] = sum;
time[x][y] = T;
for(int i = 0;i < 4;i++){
int xx = x + dx[i];
int yy = y + dy[i];
if(xx < 1||xx > n||yy < 1||yy > m||map[xx][yy] == 0||map[xx][yy]==2){
continue;
}
if(T <= 1 && map[xx][yy] == 4){//当碰见炸弹重置点时时间为0了
continue;
}
// cout<<"地图位置"<<map[xx][yy]<<endl;
if(T <= 1 && map[xx][yy] == 3){//当到达终点时,时间为0了
// cout<<"没进来?"<<endl;
continue;
}
dfs(xx,yy,sum+1,T-1);
}
return minstep;
}
int main(){
int t;
cin>>t;
int ans;
while(t--){
cin>>n>>m;
for(int i = 1;i <= n;i++){
for(int j = 1;j <= m;j++){
cin>>map[i][j];
if(map[i][j] == 2){
sx = i;
sy = j;
}
if(map[i][j] == 3){
ex = i;
ey = j;
}
}
}
memset(step,0x3f,sizeof(step));
// cout<<sx<<sy<<ex<<ey<<endl;
memset(time,0,sizeof(time));
minstep = -1;
// maxtime = 6;
ans = dfs(sx,sy,0,6);
cout<<ans<<endl;
}
return 0;
}