小兔子在一个M*N的方格的左上方,家在方格的右下方,其中部分方格有蛇,小兔子不能走有蛇的方格。小兔子每一步只能向右或向下,问小兔子回家有多少条路径?
1首先想到递归:
#define M (5)
#define N (4)
int map[M][N]={{0,0,0,1},
{0,0,1,0},
{0,0,0,0},
{0,0,1,0},
{0,0,0,0}
};
int getPathNumBydigui(int row, int col){
int pathNum = 0;
if(row==0 && col==0) return 1;
if(map[row][col] == 1) return 0;
if(IsValidPos(row-1, col)){
pathNum += getPathNumBydigui(row-1, col);
}
if(IsValidPos(row, col-1)){
pathNum += getPathNumBydigui(row, col-1);
}
return pathNum;
}
但我们知道递归的时间和空间复杂度都是指数级的,并不可取
因此采用动态规划的方法,此时的时间复杂度和空间复杂度都是M*N
bool IsValidPos(int row, int col){
if((row>=0&&row<M) && (col>=0&&col<N) && (map[row][col] != 1)) return true;
return false;
}
int getPathNumBydynamic(int row, int col){
int pathRecord[M][N] = {{1}};
for(int i=0; i<M; i++){
for(int j=0; j<N; j++){
if(IsValidPos(i-1, j)){
pathRecord[i][j] += pathRecord[i-1][j];
}
if(IsValidPos(i, j-1)){
pathRecord[i][j] += pathRecord[i][j-1];
}
}
}
return pathRecord[row][col];
}
2空间复杂度在动态规划的基础上还可以继续优化。我们注意到上面的动态规划实际记录了小兔子到任意点的路径条数。实际上我们只需要到右下方的结果,因此把二维数组pathRecord降为一维,每次只更改一行的动态规划数据
int getPathNumBydynamicWithNSpace( int col){ //不妨假设N<M,即此函数空间复杂度为O(N),此时仅保存了最后一行 各个格子的路径条数
int pathRecord[N] = {1};
for(int i=0; i<M; i++){
if(map[i][0] == 1){
pathRecord[0] = 0;
}
for(int j=1; j<N; j++){
if(map[i][j] != 1){
pathRecord[j] += pathRecord[j-1];
}
else{
pathRecord[j] = 0;
}
}
}
return pathRecord[col];
}
3,测试用例:map:
{{0,0,0,1},
{0,0,1,0},
{0,1,0,0},
{0,0,1,0},
{0,0,0,0}
};
到右下方的路径均为2
{{0,0,0,1},
{0,0,1,0},
{0,0,0,0},
{0,0,1,0},
{0,0,0,0}
};
到右下方的路径均为8
{{0,0,0,0},
{0,0,0,0},
{0,0,0,0},
{0,0,0,0},
{0,0,0,0}
};
到右下方的路径均为35
4,设计一个函数返回的是所有的路径,而不是路径的数目
考虑使用递归回溯的算法,每走一步都将坐标记录进vector中,如果当前坐标为最右下方,则保存下这条路径,在递归函数退出时需要将当前坐标从vector中拿出
vector<pair<int, int>> onePath;
vector<vector<pair<int, int>>> allPath;
void recordAllPath(int row, int col){
pair<int,int> point = {row,col};
onePath.push_back(point);
if(row == M-1 && col == N-1){
allPath.push_back(onePath);
onePath.pop_back();
return;
}
if(IsValidPos(row+1, col)){
recordAllPath(row+1, col);
}
if(IsValidPos(row, col+1)){
recordAllPath(row, col+1);
}
onePath.pop_back();
}
5,如果兔子不是只能向右或者向下,而是上下左右所有的方向都可以走,只是一个格子最多只能走一次,那么如何记录所有的路径。
这个问题与问题4类似,只是需要加一个该格子是否已经走过的判断,并且在下一步递归时,需要判断4个方向。
bool IsValidPosII(int row, int col){
if((row>=0&&row<M) && (col>=0&&col<N) && (map[row][col] != 1) && !visited[row][col]) return true;
return false;
}
void recordAllPathII(int row, int col){
pair<int,int> point = {row,col};
onePath.push_back(point);
visited[row][col] = true;
if(row == M-1 && col == N-1){
allPath.push_back(onePath);
onePath.pop_back();
visited[row][col] = false;
return;
}
if(IsValidPosII(row+1, col)){
recordAllPathII(row+1, col);
}
if(IsValidPosII(row-1, col)){
recordAllPathII(row-1, col);
}
if(IsValidPosII(row, col+1)){
recordAllPathII(row, col+1);
}
if(IsValidPosII(row, col-1)){
recordAllPathII(row, col-1);
}
onePath.pop_back();
visited[row][col] = false;
}