(重新看了一下老久以前的代码,被自己吓了一跳。。。这,字也太多了吧。。。所以如果觉得太繁琐了了不想看,最后面还有一个代码,看着可能会比较舒服)
这是一道有趣的迷宫题,也是一道搜索的题目,有点类似于深度搜索,可以通过递归来实现。由于递归很容易超时,所以务必进行“剪枝”!!!
一、当迷宫中可走的方块
二、“奇偶性剪枝”(比较常用,要记一下):如果“S”的坐标(xs,ys),“D”的坐标(xd,yd)与T满足|xs-xd|+|ys-yd|,T两者的奇偶性不相同,则一定不可能满足要求,直接输出“NO\n”,可以自己画一个有0,1构成的矩阵思考一下,所以当遇到从 0 走向 0 ,1走向1,但是要求时间是奇数的,或者, 从 1 走向 0 ,0走向1,但是要求时间是偶数的,可以直接判断不可达!
三、其它“剪枝”将于代码中解释。
思路:构造一个函数,模拟从四个方向,在迷宫中行走,因此我们需要事先写好一个数组dir[4][2],来模拟上下左右四个方向的行走。当然,必须注意到有些时候是走不了的,比如,碰到“墙”了,“越界”了,以及返回走是不行的。
代码见下:
#include<stdio.h>
#include<string.h>
#include
char s[10][10]; //用以记录迷宫
int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};//模拟上下左右移动
int flag,flag_move,move; //flag表示能否到达“D”,flag_move表示能否到达“D”的同时,使时间恰好为T,move表示走的步数
int N,M,T;
int xs,ys,xd,yd; //前两个表示“S”的坐标,后两个表示“D”的坐标
void mov(int sx,int sy) //模拟迷宫中行走的函数
{
int i,j;
if(s[sx][sy]=='X') return; //遇到墙不能走,结束此次调用
if(s[sx][sy]=='0') return; //越界了也不可以,结束此次调用
if(s[sx][sy]=='D') //找到出口“D”了
{
if(move==T) flag_move=1; 如果此时走的步数为T,flag_move=1
flag=1;return;
} // printf("%d %d __ ",sx,sy); 这是在dig bug
if(move==T)return; //达到规定的步数了,结束此次调用——这也是“剪枝”内容之一
for(i=0;i<=3;i++) //从当前位置,四个方向都走一遍
{ move++; //走一次,move+1
s[sx][sy]='X'; //走过了,就不能返回,那就把它设为“墙”
mov(sx+dir[i][0],sy+dir[i][1]); //试试向一个方向走一下
s[sx][sy]='.'; //当这个方向行不通时,或者遇到了move=T,或者已经走到了出口“D”,才会执行下面 操作,这个要理解清楚
move--; //当行不通时,move-1,因为那一步不算有效步数
if(flag&&flag_move) return; //找到了就不用再继续查找了!!!!否则会超时!!! 这是很重要的“剪枝”之一!(一次因为少了这一句,就超时了)
}
}
int main() //main函数控制输入和输出
{
int i,j,cnt; //cnt记录“X”的个数
int x,y;
while(scanf("%d %d %d",&N,&M,&T)&&N&&M&&T)
{
getchar(); //当题中输入有字符串时,要小心,这里的getchar()是用来吃掉输入N,M,T后的过行符
cnt=0; //以下几行都是初始化数据,都要在循环里面进行
xs=0;ys=0;xd=0;yd=0;move=0;flag=0;flag_move=0;
x=0;y=0;
memset(s,'0',sizeof(s));// printf("%c\n",s[2][0]);
for(i=1;i<=N;i++)
{
for(j=1;j<=M;j++)
{
scanf("%c",&s[i][j]); //如果没有上面的getchar(),这个过行符就会被当成字符串s的元素保存
if(s[i][j]=='X') cnt++;
else if(s[i][j]=='S')
{
xs=i;ys=j; //printf("%d %d S\n",xs,ys);
}
else if(s[i][j]=='D')
{
xd=i;yd=j; //printf("%d %d D\n",xd,yd);
}
// printf("%d j\n",j);
}
getchar(); //同理,吃掉输入的过行符
}
// printf("%d\n",cnt); dig bug
x=fabs(xs-xd);y=fabs(ys-yd);
if(N*M-cnt
else if((x+y)%2!=T%2) printf("NO\n"); //看“剪枝”二
else
{
mov(xs,ys);//printf("%d move %d flag\n",move,flag); dig bug
if(flag&&flag_move) printf("YES\n");
else printf("NO\n");
}
}
return 0;
}
小结:其实本题数据还是很小的,所以才可以用这个算法,否则将会超时,但没有“剪枝”的话,也会超时,所以“剪枝”真的灰常重要!
dig bug也很重要。
复杂的问题,可以考虑用自上而下的方法来编代码,用函数实现模块化的解决问题,main函数控制输入输出,其它函数表示相应功能。
/
//第三次修改。感觉以前写的代码挺一般的,现在没怎么用C/C++了都比以前写得好
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
char maze[10][10];
int dir[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
int sx, sy, dx, dy;
bool dfs(int posx, int posy, int n, int m, int left_moves)
{
if(!left_moves && posx == dx && posy == dy)
return true;
for(int i = 0; i < 4; ++i)
{
int tmpx = posx + dir[i][0], tmpy = posy + dir[i][1];
if(tmpx >= 0 && tmpx < n && tmpy >= 0 && tmpy < m) // 没有越界
{
if(maze[tmpx][tmpy] != 'X')
{
maze[posx][posy] = 'X';
if(dfs(tmpx, tmpy, n, m, left_moves - 1))
return true;
maze[posx][posy] = '.';
}
}
}
return false;
}
int main()
{
int n, m, t;
while(cin>>n>>m>>t)
{
if(!n && !m && !t)
break;
int walls_number = 0;
for(int i = 0; i < n; ++i)
{
cin>>maze[i];
for(int j = 0; j < m; ++j)
if(maze[i][j] == 'S')
{
sx = i;
sy = j;
}
else if(maze[i][j] == 'D')
{
dx = i;
dy = j;
}
else if(maze[i][j] == 'X')
walls_number++;
}
int shortest_distance = int(abs(dx + dy - sx - sy));
if(shortest_distance > t) // 剪枝1
cout<<"NO"<<endl;
else if(shortest_distance % 2 != t % 2) // 剪枝2,奇偶性剪枝
cout<<"NO"<<endl;
else if(shortest_distance > n * m - walls_number) // 剪枝3,墙太多
cout<<"NO"<<endl;
else if(dfs(sx, sy, n, m, t))
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
}
return 0;
}