这题的题目不再赘述,题目链接https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4475。
题目意思和解题思路都很明显,用BFS求点到点的单源最短路径。但是不同的是:有些格子有障碍,但是又不能连续地穿过k个障碍。所以解题的关键就变成了如何判断当前层次访问的格子穿过的障碍小于等于k。我用了两种相似的思路来解这道题。
先说第一种。不多说,总体BFS。格子数据结构定义有x(横坐标)、y(纵坐标)、level(层次,用于得出最短路径)、layer(穿过的障碍层数)。首先由上层格子遍历周围四个方向的格子(得到当前格子),并判断坐标是否合法。然后判断该格子是否已经访问。接着判断当前层次访问格子穿过障碍是否小于等于k:首先判断当前格子是否有障碍,如果没有,layer为0;如果有,则layer等于上一层格子的layer+1。将格子入队。最后设置当前格子已经访问。代码如下:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<queue>
using namespace std;
struct Vertex
{
int x;
int y;
int level;
int layer;
Vertex(int _x,int _y,int le,int la):x(_x),y(_y),level(le),layer(la){};
};//格子的数据结构定义
const int maxn=25;
int T;
int m,n;
int k;
int map[maxn][maxn];
int visited[maxn][maxn];
int dir[4][2]={{1,0},{0,1},{-1,0},{0,-1}};//二维向量,格子在四个方向上的坐标增量
int BFS()
{
int i;
queue<Vertex> qu;
qu.push(Vertex(0,0,1,0));
visited[0][0]=1;
while(!qu.empty())
{
Vertex v=qu.front();
qu.pop();
if(v.x==m-1&&v.y==n-1)
return v.level-1;
for(i=0;i<4;i++)
{
int dx=v.x+dir[i][0];
int dy=v.y+dir[i][1];
if(dx>=0 && dx<m && dy>=0 && dy<n)
{//坐标合法
if(visited[dx][dy]==0)
{//该点尚未访问
visited[dx][dy]==1;
if(map[dx][dy]==0)
{//没有障碍
qu.push(Vertex(dx,dy,v.level+1,0));
}
else
{//有障碍
if((v.layer+1)<=k)
{
qu.push(Vertex(dx,dy,v.level+1,v.layer+1));
}
}
}
}
}
}
return -1;
}
int main()
{
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
scanf("%d",&T);
while(T--)
{
memset(visited,0,sizeof(visited));
scanf("%d%d",&m,&n);
scanf("%d",&k);
int i;
int j;
for(i=0;i<m;i++)
{
for(j=0;j<n;j++)
scanf("%d",&map[i][j]);
}
printf("%d\n",BFS());
}
}
第二种方法。整体思路和第一种非常相似。但是,这里的visited访问数组增加了一个维度,变成了三维的。第三个维度表示的意思是穿过的障碍层数。这种方法似乎不是很好理解。不妨把它想象成一幢只打好地基房子。最表面的砖头就是一个个的格子,并且标明了是否有障碍。接着要做的就是在地基之上堆砌砖头。如果砖头上表明没有障碍,就不往上堆砖头(如果上面有多余一个的砖头的话就把上面的砖头都扔了);如果有障碍,就在当前格子上以前一个格子的砖头数为基础再增加一个砖头。但是堆的砖头数不能超过k个。代码如下:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<queue>
using namespace std;
struct Vertex
{
int x;
int y;
int level;
int layer;
Vertex(int _x,int _y,int le,int la):x(_x),y(_y),level(le),layer(la){};
};//格子的数据结构定义
const int maxn=25;
int T;
int m,n;
int k;
int map[maxn][maxn];
int visited[maxn][maxn][maxn];//visited增加了一个维度
int dir[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
int BFS()
{
int i;
queue<Vertex> qu;
qu.push(Vertex(0,0,1,0));
visited[0][0][0]=1;
while(!qu.empty())
{
Vertex v=qu.front();
qu.pop();
if(v.x==m-1&&v.y==n-1)
return v.level-1;
for(i=0;i<4;i++)
{
int dx=v.x+dir[i][0];
int dy=v.y+dir[i][1];
int layer=v.layer;
if(dx>=0 && dx<m && dy>=0 && dy<n)
{//坐标合法
if(map[dx][dy]==0) layer=0;//格子没有障碍
else layer++;
if(layer<=k && visited[dx][dy][layer]==0)
{//穿过的障碍层数小于等于k并且尚未访问
qu.push(Vertex(dx,dy,v.level+1,layer));
}
visited[dx][dy][layer]=1;//置为已访问
}
}
}
return -1;
}
int main()
{
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
scanf("%d",&T);
while(T--)
{
memset(visited,0,sizeof(visited));
scanf("%d%d",&m,&n);
scanf("%d",&k);
int i;
int j;
for(i=0;i<m;i++)
{
for(j=0;j<n;j++)
scanf("%d",&map[i][j]);
}
printf("%d\n",BFS());
}
}
两种方法的思路都几乎相同。而且都基于BFS。所以算法的时间复杂度大致也应该是一个量级的才对。不过为什么第一种方法TLE了,我到现在都还没明白,欢迎各位路过的给我留言。本人不打ACM,刷题纯粹是为了学习算法的需要。所以很多东西都不是很懂,欢迎指教。