who knows the truth。
Fork me on GitHub
返回顶部

迷宫问题及延伸

迷宫问题

问题描述:给定一个M×N的迷宫图,求所有从指定入口 到出口的路径。假设迷宫图如下图所示(其中M=6,N=6,含 外围加上一圈不可走的方块,这样做的目的是避免在查找时 出界),迷宫由方块构成,空白方块表示可以走的通道,带 阴影方块表示不可走的障碍物。 要求所求路径必须是简单路径,即在求得的路径上不能 重复出现同一空白方块,而且从每个方块出发只能走向上下 左右四个相邻的空白方块。

用回溯法解决迷宫问题

回溯法=dfs+剪枝函数

回溯法实际上一个类似穷举的搜索尝试过程,主要是 在搜索尝试过程中按照深度优先的方法寻找问题的解,当 发现已不满足求解条件时,就“回溯(Backtracking)” (即回退,走不通就掉头),尝试别的路径。

回溯法在问题的解空间树中,按深度优先策略,从根结 点出发搜索解空间树。 算法搜索至解空间树的任意一点时,先判断该结点是否 包含问题的解。如果肯定不包含,则跳过对该结点为根 的子树的搜索,逐层向其祖先结点回溯;否则,进入该 子树,继续按深度优先策略搜索。

回溯法在用来求问题的所有解(或最优解)时,要回 溯到根,且根结点的所有子树都已被搜索遍才结束。 • 而在求任一解(存在性问题)时,只要搜索到一个解 就结束。 • 这种以深度优先(DFS)的方式系统地搜索问题的解的 算法称为回溯法,它适合于解一些组合数较大的问题。

在解决迷宫问题时,需要注意:1.结束  当起止点坐标等于终点 2.走过的点一定要标记,否则会再回溯过去 3.设置一个函数能控制点上下左右四个方向 4.采用回溯时一定要清理数据。恢复路径(把走过的点恢复为0等)。

代码

//迷宫问题
#include<stdio.h>
#include<stdbool.h>
#define row 10
#define col 10
int pathlen=0;
int path[row*col];
int maze[row][col] = {
// 0 3 5 7 9
{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{-1, 0, 0, 0, -1, 0, 0, -1, 0, -1}, //1
{-1, 0, -1, 0, -1, -1, 0, 0, 0, -1},
{-1, 0, 0, 0, -1, -1, 0, -1, -1, -1}, //3
{-1, 0, -1, 0, 0, 0, 0, 0, 0, -1},
{-1, 0, -1, -1, -1, -1, -1, -1, -1, -1}, //5
{-1, 0, 0, 0, -1, 0, -1, 0, 0, -1},
{-1, 0, -1, -1, -1, 0, 0, 0, -1, -1}, //7
{-1, 0, 0, 0, 0, 0, -1, 0, 0, -1},
{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}
// 0 3 5 7 9
};
void print_maze();
void print_path(int,int);
int make_new_place(int,int,int);
bool dfs(int,int,int,int);
int main(void)
{
int start_r,start_c,end_r,end_c;
printf("起始坐标\n");
scanf("%d %d",&start_r,&start_c);
if(maze[start_r][start_c]!=0)
{
printf("位置错误\n");
return 0;}
printf("终点坐标\n");
scanf("%d %d",&end_r,&end_c);
if(maze[end_r][end_c]!=0)
{
printf("位置错误\n");
return 0;
}
maze[start_r][start_c]=-2;
print_maze();
dfs(start_r,start_c,end_r,end_c);
return 0;
}
void print_maze()
{
int r,c;
for(r=0;r<row;r++)
{
printf("%d ",r);
for(c=0;c<col;c++)
{
if(maze[r][c]==-1)
printf("%s","==");
else
if(maze[r][c]==0)
printf("%s","**");
else
printf("%d",maze[r][c]);

}
printf("\n");
}
printf("\n");
return;
}
void print_path(int start_r,int start_c)
{
int place_r,place_c;
int i;
printf("end:%d %d\n",start_r,start_c);
printf("steps:%d\n",pathlen);
for(i=0;i<pathlen;i++)
{
place_r=path[i]/col;
place_c=path[i]%col;
printf("->(%d %d)\n",place_r,place_c);
}
return;
}
int make_new_place(int place_r,int place_c,int director)
{
int ret_place=-1;
int new_r=place_r;
int new_c=place_c;
switch(director)
{
case 0:new_r--;break;
case 1:new_c++;break;
case 2:new_r++;break;
case 3:new_c--;break;
}
if(maze[new_r][new_c]==0)
ret_place=new_r*col+new_c;
if(maze[new_r][new_c]==-2)
ret_place=-2;
return ret_place;
}
bool dfs(int start_r,int start_c,int end_r,int end_c)
{
int place_r,place_c;
int i;
bool result=false;
if(start_r==end_r&&start_c==end_c)
{
print_path(start_r,start_c);
return true;
}
int new_p;
for(i=0;i<4;i++)
{
new_p=make_new_place(start_r,start_c,i);
if(new_p!=-1&&new_p!=-2)
{
path[pathlen]=new_p;
pathlen++;
place_r=new_p/col;
place_c=new_p%col;
maze[place_r][place_c]=99;
// print_maze();
result=dfs(place_r,place_c,end_r,end_c);

//恢复数据
maze[place_r][place_c]=0;
path[pathlen]=0;
pathlen--;

}
}
return result;
}

骑士遍历问题

即设置一个棋盘,设置一个起点,让中国象棋中的马遍历整个棋盘。

我的做法是 当马遍历整个棋盘时因为不能重复走,则走的次数等于棋盘格子数-1.

当达到这个条件输出结果。

其他与迷宫问题类似

注意:可以跟迷宫问题一样,设置一个数组,数组大小与棋盘一样,不初始化的话每个值为0,走过一个点,将这个点设置一个值。这样使这个点不会再被走过。还可以走过一个点后,将这个点标记,利用数组标记为1,。两种方法原理类似。马有8种可能的方向,注意约束。

#include<stdio.h>
#include<stdbool.h>
#include<windows.h>
#define N 20
int len;
int path_len=0;
int a_mark[N][N];
int solution=0;
int path[N*N];
void mark(int);
void unmark(int);
void print_path();
int make_new_place(int,int,int);
bool dfs(int,int);
int main(void)
{
int start_r,start_c;
scanf("%d",&len);
scanf("%d %d",&start_r,&start_c);
a_mark[start_r][start_c]=1;
printf("正在解中...\n");
Sleep(10);
dfs(start_r,start_c);
printf("有%d种解法\n",solution);
return 0;
}
bool dfs(int start_r,int start_c)
{
bool result=false;

if(path_len==len*len-1)
{
solution++;
print_path();
return true;
}
else
{
int new_p;
int i;
for(i=0;i<8;i++)
{
new_p=make_new_place(start_r,start_c,i);
if(new_p!=-1)
{
int place_r,place_c;
mark(new_p);
place_r=new_p/len;
place_c=new_p%len;
path[path_len]=new_p;
path_len++;
result=dfs(place_r,place_c);
path[path_len]=0;
unmark(new_p);
path_len--;
}
}
}
return result;

}
int make_new_place(int start_r,int start_c,int i)
{
int ret=-1;
int new_r=start_r;
int new_c=start_c;
switch(i)
{
case 0:new_r-=2;new_c+=1;break;
case 1:new_r-=1;new_c+=2;break;
case 2:new_r+=1;new_c+=2;break;
case 3:new_r+=2;new_c+=1;break;
case 4:new_r+=2;new_c-=1;break;
case 5:new_r+=1;new_c-=2;break;
case 6:new_r-=1;new_c-=2;break;
case 7:new_r-=2;new_c-=1;break;
}
if(new_r>=0&&new_r<len)
if(new_c>=0&&new_c<len)
if(a_mark[new_r][new_c]==0)
ret=new_r*len+new_c;
return ret;
}
void mark(int i)
{
int s,j;
s=i/len;
j=i%len;
a_mark[s][j]=1;
return;
}
void unmark(int i)
{
int s,j;
s=i/len;
j=i%len;
a_mark[s][j]=0;
return;
}
void print_path()
{
int i;
for(i=0;i<len*len-1;i++)
{
int s=path[i]/len;
int j=path[i]%len;
printf("->(%d %d)",s,j);
}
printf("\n");
}

延伸一下,若要求出两点最短的路径。

此时结束条件设置为起始点与终点相同。若要输出最短路径,意思是要保留上一次路径的数据。我设置了一个数组保留每种解法所需要的步数。如果新的步数更小则将此时的最短路径刷新,若新的步数更大,则替换,使此时的步数最小。

#include<stdio.h>
#include<stdbool.h>
#define N 51
#define M 1000
int len;
int pathlen=0;
int mark[N][N];
int path[N*N];
int best_path[N*N];
int m[M];
int solution=0;
bool dfs(int,int,int,int);
void print_path(int);
void print_best(int);
int make_new_place(int,int,int);
int main(void)
{
int start_r,start_c,end_r,end_c;
scanf("%d",&len);
scanf("%d %d",&start_r,&start_c);
scanf("%d %d",&end_r,&end_c);
dfs(start_r,start_c,end_r,end_c);
printf("ÓÐ%dÖֽⷨ\n",solution);
printf("the best way is");
int k;
for(k=0;k<m[solution];k++)
{
int r,c;
r=best_path[k]/len;
c=best_path[k]%len;
printf("->(%d %d)",r,c);
}
printf("\n");
return 0;
}
bool dfs(int start_r,int start_c,int end_r,int end_c)
{
bool result;
if(start_r==end_r&&start_c==end_c)
{
solution++;
m[solution]=pathlen;
print_path(pathlen);
return true;
}
int i;
for(i=0;i<8;i++)
{
int new_p;
new_p=make_new_place(start_r,start_c,i);
if(new_p!=-1)
{
int r,c;
r=new_p/len;
c=new_p%len;
path[pathlen]=new_p;
pathlen++;
mark[r][c]=1;
result=dfs(r,c,end_r,end_c);
path[pathlen]=0;
mark[r][c]=0;
pathlen--;
}
}
return result;
}
void print_path(int limit)
{
int i;
for(i=0;i<pathlen;i++)
{
int r,c;
r=path[i]/len;
c=path[i]%len;
printf("->(%d %d)",r,c);
}
printf("\n");
if(solution==1)
{
int k;
for(k=0;k<limit;k++)
best_path[k]=path[k];
return;
}
else
{
if(m[solution]<m[solution-1])
{
print_best(m[solution]);
}
else
{
int temp;
temp=m[solution];
m[solution]=m[solution-1];
m[solution-1]=temp;
}

}
return;
}
void print_best(int limit)
{
int i;
for(i=0;i<limit;i++)
{
best_path[i]=path[i];
}
}
int make_new_place(int start_r,int start_c,int i)
{
int ret=-1;
int new_r=start_r;
int new_c=start_c;
switch(i)
{
case 0:new_r-=2;new_c+=1;break;
case 1:new_r-=1;new_c+=2;break;
case 2:new_r+=1;new_c+=2;break;
case 3:new_r+=2;new_c+=1;break;
case 4:new_r+=2;new_c-=1;break;
case 5:new_r+=1;new_c-=2;break;
case 6:new_r-=1;new_c-=2;break;
case 7:new_r-=2;new_c-=1;break;
}
if(new_r>=0&&new_r<len)
if(new_c>=0&&new_c<len)
if(mark[new_r][new_c]==0)
ret=new_r*len+new_c;
return ret;
}

 

posted @ 2020-10-07 22:18  no_sense  阅读(223)  评论(0编辑  收藏  举报
欢迎来到kanashimi的博客
pic
kanashimi
真理将引领我们