题干:有一个矩形的房间,覆盖着方砖。 每个瓷砖都是红色或黑色。 一个男人站在黑色的瓷砖上,他可以移动到四个相邻的瓷砖之一。 但他不能在红砖上移动,他只能在黑砖上移动。
编写一个程序来计算他可以通过重复上述移动来达到的黑色瓦片的数量。
Input
输入由多个数据集组成。一个数据集以一条包含两个正整数W和H的线开始;W和H分别是x和y方向上的方块数。W和H不超过20。
There are H more lines in the data set, each of which includes W characters. Each character represents the color of a tile as follows.
‘.’ - a black tile
‘#’ - a red tile
‘@’ - a man on a black tile(appears exactly once in a data set)
The end of the input is indicated by a line consisting of two zeros.
这里可以用到递归和回溯的思想,主要的递归体现在search()中的自我调用,
search(row-1,col);
//向上移动一格
search(row+1,col);
//向下移动一格
search(row,col-1);
//向左移动一格
search(row,col+1);
//向右移动一格
将位置递给search之后进行判断看是不是符合题目的条件,既是否能够按照递给search的位置移动
#include <iostream>
#include <string>
using namespace std;
const int maxn=20+5,maxm=20+5;//用设置数据的上限
int n,m,ans;
string map[maxn];
bool visited[maxn][maxm];
void search(int row,int col)
{
if(row<0||row>=n||col<0||col>=m||map[row][col]=='#'||visited[row][col]==true)
return ;
visited[row][col]=true;
++ans;
search(row-1,col);
search(row+1,col);
search(row,col-1);
search(row,col+1);
}
int main(void) {
cin>>m>>n;
while(n||m){
int row,col;
for(int i=0;i<n;i++){
cin>>map[i];
for(int j=0;j<m;j++)
if(map[i][j]=='@'){//记下男子开始的位置
row=i;
col=j;
}
}
memset(visited, false, sizeof(visited));//将每个瓷砖初始化
ans=0;
search(row,col);//开始进入判断递归计算,从男子当前的位置开始
cout<<ans<<endl;
cin>>m>>n;
}
}
在使用回溯法解题时,一般需要结合题意考虑如下因素:
[1]定义状态:即如何描述问题求解过程中每一步的状况。为了精简程序,增加可读性,我们一般将参写子状态扩展运算的变量组合成当前状态列入值参或局部变量,以便回溯时能恢复递归前的状态,重新计算下一条路行径。若这些参数的存储量大例如数组,为避免内存溢出,则必须将其设为至局变量,且回溯前需恢复具递归前的值。
[2]边界条件:即在什么情况下,程序不再递归下去。如果是求满足某个特定条件的一条最佳路径,则当前状态到达边界时并非一定意味着此时就是最雀目标状态,因此还须增茄判别最优目标状态的条件。
[3] 搜索范围:若当前子状态不满足边界条件,则扩展子状态。在这种情况下,应如何设计扩展子状态的算符值范围。换句话说,如何设定for语句中循环变量的初值和终值。
[4]约束条件和最优性要求:扩展 出的子状态应满足什么条件方可继续递归下去,如果是求满足某个特定条件的一条最佳路径,那么在扩展出某子状态后是否继续递归搜索下去菜不仅取决于子状态是否满足药束条件,而且还取决于子状态是否满足最优性要求。