迷宫游戏
我们用一个二维的字符数组来表示前面画出的迷宫:
其中字符S表示起点,字符T表示终点,字符*表示墙壁,字符.表示平地。你需要从S出发走到T,每次只能向上下左右相邻的位置移动,不能走出地图,也不能穿过墙壁,每个点只能通过一次。你需要编程来求解一种从起点到终点的走法。
C语言代码:
#include<iostream>
#include<string>
using namespace std;
int m,n;
bool vis[110][110];
string maze[110];
bool in(int x,int y){//判断当前点在不在地图内
return x>=0&&x<n&&y>=0&&y<m;
}
bool dfs(int x,int y){//从x,y点往下搜索
if(maze[x][y]=='T'){//找到了终点返回true
return true;
}
vis[x][y]=1;//标记此点被找过了
maze[x][y]='m';//标记寻找路线
int tx=x-1,ty=y;//向上搜索
if(in(tx,ty)&&maze[tx][ty]!='*'&&!vis[tx][ty]){//看是否满足条件
if(dfs(tx,ty)){//满足条件返回true
return true;
}
}
tx=x;ty=y-1;//向左搜索
if(in(tx,ty)&&maze[tx][ty]!='*'&&!vis[tx][ty]){
if(dfs(tx,ty))
return true;
}
tx=x+1,ty=y;//向下搜索
if(in(tx,ty)&&maze[tx][ty]!='*'&&!vis[tx][ty]){
if(dfs(tx,ty)){
return true;
}
}
tx=x,ty=y+1;//向右搜索
if(in(tx,ty)&&maze[tx][ty]!='*'&&!vis[tx][ty]){
if(dfs(tx,ty)){
return true;
}
}
vis[x][y]=0;
maze[x][y]='.';//做到不满足条件的地方回溯,取消此点标记
return false;
}
int main(){
cin>>n>>m;
for(int i=0;i<n;i++){//输入地图
cin>>maze[i];
}
int x,y;
for(int i=0;i<n;i++){//找到起点
for(int j=0;j<m;j++){
if(maze[i][j]=='S'){
x=i,y=j;
}
}
}
if(dfs(x,y)){//从起点开始搜索
for(int i=0;i<n;i++)
cout<<maze[i]<<endl;//打印地图
}else{
cout<<"NO!"<<endl;
}
return 0;
}
运行结果:
我们会发现,代码比较长,而且四个方向的代码其实一样,这里我们就可以通过一个数组来进行优化,表示四个方向
int dir[4][2]={{-1,0},{0,-1},{1,0},{0,1}};
这样来选择每个方向:
for(int i=0;i<4;i++){
int tx=x+dir[i][0];
int ty=y+dir[i][1];
if(in(tx,ty)&&maze[tx][ty]!='*'&&!vis[tx][ty]){
if(dfs(tx,ty)){
return true;
}
}
}
优化后的代码:
#include<iostream>
#include<string>
using namespace std;
int m,n;
bool vis[110][110];
int dir[4][2]={{-1,0},{0,-1},{1,0},{0,1}};
string maze[110];
bool in(int x,int y){//判断当前点在不在地图内
return x>=0&&x<n&&y>=0&&y<m;
}
bool dfs(int x,int y)
{
if(maze[x][y]=='T'){
return true;
}
vis[x][y]=1;
maze[x][y]='m';
for(int i=0;i<4;i++){
int tx=x+dir[i][0];
int ty=y+dir[i][1];
if(in(tx,ty)&&maze[tx][ty]!='*'&&!vis[tx][ty]){
if(dfs(tx,ty)){
return true;
}
}
}
vis[x][y]=0;
maze[x][y]='.';
return false;
}
int main(){
cin>>n>>m;
for(int i=0;i<n;i++){//输入地图
cin>>maze[i];
}
int x,y;
for(int i=0;i<n;i++){//找到起点
for(int j=0;j<m;j++){
if(maze[i][j]=='S'){
x=i,y=j;
}
}
}
cout<<endl;
if(dfs(x,y)){//从起点开始搜索
for(int i=0;i<n;i++)
cout<<maze[i]<<endl;//打印地图
}else{
cout<<"NO!"<<endl;
}
return 0;
}
运行结果:
中国象棋:
中国象棋博大精深,其中马的规则最为复杂,也是最难操控的一颗棋子。
我们都知道象棋中马走日,并且存在撇马脚的,现在要求输入m行n列棋局,S表示马的起点,T 表示马的终点,#表示这里有一颗棋子,.表示空地,现在让你模仿马的行走,判断是否可以走到终点,可以的化输出行走路径,不行的话输出NO;
C语言代码:
#include<string>
#include<iostream>
using namespace std;
int m,n;
string map[110];
bool vis[110][110];//记录是否走过;
int dir[8][2]={{2,1},{1,2},{-1,2},{-2,1},{-2,-1},{-1,-2},{1,-2},{2,-1}} ;
int dir_[8][2]={{1,0},{0,1},{0,1},{-1,0},{-1,0},{0,-1},{0,-1},{1,0}};//蹩马腿
bool in(int x,int y){
return x<m&&x>=0&&y>=0&&y<n;
}
bool dfs(int x,int y){
if(map[x][y]=='T'){
return true;
}
vis[x][y]=true;
map[x][y]='m';
for(int i=0;i<8;i++){
int tx=x+dir[i][0];
int ty=y+dir[i][1];
if(in(tx,ty)&&map[tx][ty]!='#'&&!vis[tx][ty]&&map[x+dir_[i][0]][y+dir_[i][1]]=='.'){//最后一个是判断是否蹩马腿
if(dfs(tx,ty)){
return true;
}
}
}
vis[x][y]=false;
map[x][y]='.';
return false;
}
int main()
{
int x,y;//m行n列
cin>>m>>n;
for(int i=0;i<m;i++){
cin>>map[i];
}
for(int i=0;i<m;i++){//寻找起点
for(int j=0;j<n;j++){
if(map[i][j]=='S'){
x=i;
y=j;
}
}
}
cout<<endl;
if(dfs(x,y)){
for(int i=0;i<m;i++){
cout<<map[i]<<endl;
}
}else{
cout<<"NO!";
}
return 0;
}
这种只需要找到的深搜思想我们可以理解为:一个军营派出很多士兵去寻找敌人,有的士兵半路就牺牲了(这一部分是不符合条件的)就会return false告诉上面的士兵这条路走不通,一旦有士兵找到,就return true给上面发信号,上面收到信号第一时间停止所有士兵的寻找。并且报告给主函数找到了。
接下来我们处理边界的情况,假设马一定可以到达终点,但是有很多种走法,我们需要输出花费时间最短的步数怎么办?
我们可以定义一个全局变量来记录一个最小值,初始的时候,ans要赋值成为一个很大的数。并且dfs需要一个参数step来记录步数:
#include<string>
#include<iostream>
using namespace std;
int m,n;
string map[110];
bool vis[110][110];//记录是否走过;
int ans=100000000;
int dir[8][2]={{2,1},{1,2},{-1,2},{-2,1},{-2,-1},{-1,-2},{1,-2},{2,-1}} ;
int dir_[8][2]={{1,0},{0,1},{0,1},{-1,0},{-1,0},{0,-1},{0,-1},{1,0}};//蹩马腿
bool in(int x,int y){
return x<m&&x>=0&&y>=0&&y<n;
}
void dfs(int x,int y,int step){
if(map[x][y]=='T'){
if(step<ans){
ans=step;
}
return;
}
vis[x][y]=true;
for(int i=0;i<8;i++){
int tx=x+dir[i][0];
int ty=y+dir[i][1];
if(in(tx,ty)&&map[tx][ty]!='#'&&!vis[tx][ty]&&map[x+dir_[i][0]][y+dir_[i][1]]=='.'){//最后一个是判断是否蹩马腿
dfs(tx,ty,step+1);
}
}
vis[x][y]=false;
}
int main()
{
int x,y;//m行n列
cin>>m>>n;
for(int i=0;i<m;i++){
cin>>map[i];
}
for(int i=0;i<m;i++){//寻找起点
for(int j=0;j<n;j++){
if(map[i][j]=='S'){
x=i;
y=j;
}
}
}
dfs(x,y,0);
cout<<ans<<endl;
}
这种深搜思想我们可以理解为:一个军营派出很多士兵去寻找敌人,有的士兵半路就牺牲了(这一部分是不符合条件的),有的士兵找到了敌人就报上了自己花了多少步,然后发出return命令让他休息(要不然他会接着找),长官将步数进行对比并且比出最小的步数,到所有的士兵牺牲的牺牲休息的休息,那寻找也就结束了。
象棋土木改编,输入两数m,n代表网格的大小,再输入x,y代表马目前所在的位置,问此时的马三步之内可以到哪些地方,用#表示出来
分析:用一个step来记录步数,当步数大于3时return停掉即可。
#include<iostream>
#include<string>
using namespace std;
int dir[8][2]={{-2,-1},{-2,1},{2,-1},{2,1},{1,2},{1,-2},{-1,2},{-1,-2}} ;
char mp[10005][10005];
int m,n;
void dfs(int x,int y,int step){
if(step>3){
return;
}
if(x<0||x>=m||y<0||y>=n){
return;
}
mp[x][y]='#';
for(int i=0;i<8;i++){
int tx=x+dir[i][0];
int ty=y+dir[i][1];
dfs(tx,ty,step+1);
}
}
int main(){
int x,y;
cin>>m>>n;
cin>>x>>y;
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
mp[i][j]='.';
}
}
dfs(x-1,y-1,0);
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
cout<<mp[i][j];
}
cout<<endl;
}
return 0;
}
运行结果: