这一题是求最短路问题,我的第一想法是使用BFS求解,其实使用DFS求解也是可以的,DFS的代码还要简单一些,后面将会给出两种方式的代码。先说一下BFS的求解过程,这题与普通的BFS迷宫最短路问题有些区别,普通的最短路问题障碍物是不能通过的,而这题障碍物也是可以通过的,只是连续通过的障碍物个数不能超过k,所以结点入队时的值(这里的值指该结点连续通过障碍物的个数)的计算要复杂一点,因为每个位置所对应的结点连续通过障碍物的个数都应是最小的,最小个数不能简单的拿队首结点连续通过障碍物的个数加一,因为队首结点连续通过障碍物的个数加一不一定是最小的,方法应该是根据当前位置是否为障碍物来求解,如果不是障碍物,则当前结点连续通过障碍物的个数设为0,否则取该结点对应的位置上下左右四个结点中已经访问过的结点,在这些结点中取连续障碍物个数最小的,然后加一即可,BFS具体代码如下:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
struct Node
{
int r;
int c;
int obs; //已连续通过的障碍物个数
int steps; //当前步数
Node(int r,int c,int o,int s):r(r),c(c),obs(o),steps(s){}
};
queue<Node> q;
const int N=20+10;
int m,n,k;
int maze[N][N]; //迷宫
int obs[N][N]; //存储到达迷宫对应位置所经过障碍物个数的最小值
int visit[N][N]; //0未访问,1访问过
int dir[4][2]={{-1,0},{0,1},{1,0},{0,-1}};
int getMinObs(int r,int c) //得到(r,c)结点的周围最小的障碍物个数
{
int minObs=999999;
for(int i=0;i<4;i++)
{
int nr=r+dir[i][0];
int nc=c+dir[i][1];
if(visit[nr][nc])
minObs=min(minObs,obs[nr][nc]);
}
return minObs;
}
void bfs()
{
while(!q.empty())
{
Node node=q.front();
q.pop();
if(node.r==m&&node.c==n)
{
cout<<node.steps<<endl;
return;
}
for(int i=0;i<4;i++)
{
int nextr=node.r+dir[i][0];
int nextc=node.c+dir[i][1];
if(nextr==m&&nextc==n)
{
cout<<node.steps+1<<endl;
return;
}
if(nextr>=1&&nextr<=m&&nextc>=1&&nextc<=n&&!visit[nextr][nextc])
{
if(maze[nextr][nextc]==1)
{
int minObs=getMinObs(nextr,nextc);
if(minObs+1<=k)
{
obs[nextr][nextc]=minObs+1;
visit[nextr][nextc]=1;
q.push(Node(nextr,nextc,minObs+1,node.steps+1));
}
}
if(maze[nextr][nextc]==0)
{
obs[nextr][nextc]=0;
visit[nextr][nextc]=1;
q.push(Node(nextr,nextc,0,node.steps+1));
}
}
}
}
cout<<-1<<endl;
}
int main()
{
// freopen("test.txt","r",stdin);
int t;
cin>>t;
while(t--)
{
while(!q.empty()) q.pop();
memset(maze,0,sizeof(maze));
memset(visit,0,sizeof(visit));
cin>>m>>n>>k;
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
scanf("%d",&maze[i][j]);
if(maze[1][1]==1)
q.push(Node(1,1,1,0));
else q.push(Node(1,1,0,0));
visit[1][1]=1;
bfs();
}
return 0;
}
使用DFS来求解本题代码要更简单一点,DFS求解与BFS求解的主要区别是标志是否访问过某位置的visit数组改成了到某位置所需要的路径长度dist数组,需要注意递归的条件,具体实现代码如下:
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int N=20+10;
int maze[N][N]; //迷宫
int dist[N][N][50]; //dist[r][c][obs]
int m,n,k;
int dir[4][2]={{-1,0},{0,1},{1,0},{0,-1}};
bool hasAns=false; //是否存在路径
int minDist; //最短路径的长度
void dfs(int r,int c,int obs,int steps)
{
if(r==m&&c==n)
{
hasAns=true;
minDist=min(steps,minDist);
return;
}
for(int i=0;i<4;i++)
{
int nextr=r+dir[i][0];
int nextc=c+dir[i][1];
int obst=obs;
if(nextr>=1&&nextr<=m&&nextc>=1&&nextc<=n)
{
if(maze[nextr][nextc]==0)
obst=0;
else obst++;
if(obst<=k&&(steps+1<dist[nextr][nextc][obst]||dist[nextr][nextc][obst]<0)) //注意条件
{
dist[nextr][nextc][obst]=steps+1;
dfs(nextr,nextc,obst,steps+1);
}
}
}
}
int main()
{
//freopen("dfs.txt","r",stdin);
int t;
cin>>t;
while(t--)
{
minDist=999999;
hasAns=false;
memset(maze,0,sizeof(maze));
memset(dist,-1,sizeof(dist));
cin>>m>>n>>k;
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
scanf("%d",&maze[i][j]);
if(maze[1][1]==1){
dist[1][1][1]=0;
dfs(1,1,1,0);
}
else{
dist[1][1][0]=0;
dfs(1,1,0,0);
}
if(!hasAns)
cout<<-1<<endl;
else cout<<minDist<<endl;
}
return 0;
}