题目如下:
做题历程:
看到题目时,第一个想法就是回溯,虽然出发点是对的,但是干了几个小时依然卡在了一个地方,就是标记的迷宫点是时刻变化的,当你这条路线走完后又要回到之前的点时,你标记的点又要从走过变为没有走过,回溯实际上就是参数传递再返回,而因为对回溯的不熟悉,不知道如何使标记变量在参数返回时跟着同步变化(真的想了好久啊),在最后实在想不出时看了资料,最后卡住的这点终于得到解决。
解题思路:
当确定S坐标后,从S坐标开始上下左右走,需要判断走的这点是否符合要求,如果符合要求,继续往下走,直到到达T点为止,记为一次走法,参数返回,从另一个分支开始走,若能到达T点,走法加1,参数再返回…
其中回溯有个特点是我之前不了解的,例如
void mylist(int p)
{
if() sum++;
for(int i=0;i<n;i++)
{
...
mylist(p)
...
}
}
当符合条件时,mylist() 会一直进行下去,但当条件不符合for()循环,结束时便会返回到前一个for()循环中,这样,就会执行mylist()下面的语句,这也是同步迷宫坐标标记和参数返回的关键点。
个人对回溯特点的理解即是:
只要符合条件就继续调用原来函数执行,当条件都不符合不能继续调用原函数时,返回到上一级往下执行语句。
就像一个小泥鳅,只要有洞就会往里面钻,钻到底后发现没洞了,就会往上爬在四周继续寻找洞口。(接受反驳和建议)
解题代码:
#include<iostream>
#include<algorithm>
using namespace std;
struct walk
{
int x;
int y;
}S; //记录点的坐标
int t[4][2]={{1,0},{-1,0},{0,1},{0,-1}}; //上下左右四个方向,4行2列,0列代表x,1列代表y
int n,m; // 输入的n行m列,设置在外面方便调用,下面同理
char s[100][100]; //用来记录迷宫各点
int sum=0; //用来统计走法
bool flag[100][100]; //用来标记该点走没走过
void areval(walk p)
{
if(s[p.x][p.y]=='T')
{
sum++; //若有点的坐标与T坐标相等,则说明此走法可行,加1
}
for(int i=0;i<4;i++) //上左下右四个点
{
walk q; //设一个新的点
q.x=p.x+t[i][0];
q.y=p.y+t[i][1];
if(q.x>=n||q.x<0||q.y>=m||q.y<0) continue; //判断改点是否越界
if(s[q.x][q.y]=='#'||flag[q.x][q.y]!=true) continue; //判断改点是否能走或者是否走过
flag[p.x][p.y]=false; //将原来的 p 点标记为走过
//cout<<"p["<<q.x<<"]["<<q.y<<"]\n";(用来观察点的走势)
areval(q); //不断调用回溯
flag[p.x][p.y]=true; //参数返回时使得原来的点恢复为未走过
}
}
int main()
{
while(cin>>n>>m)
{
walk S,T;
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
cin>>s[i][j];
if(s[i][j]=='S')
{
S.x=i;S.y=j;
}
}
}
fill(&flag[0][0],&flag[n-1][m-1]+1,true); //先初始化,使该迷宫点都没走过
flag[S.x][S.y]=false; //标记初点,使其走过
areval(S); //调用函数
if(sum==0) cout<<"impossible";
else cout<<sum;
sum=0;
cout<<endl;
}
return 0;
}
感想:
几个小时只做了一题,在高中这个样子作业是写不完的,哈哈哈,不过收获确实也挺多的,之前做n皇后问题以为对回溯掌握了,但是其实只掌握了思路,但一些细节却没研究透彻,这题能完善我的理论,挺好。