题面
Description
Input
Output
Sample Input & Sample Output
【样例输入1】
4 5
##...
..1#@
432#.
...#.
【样例输出1】
4
【样例输入2】
4 4
#78#
.612
.543
..@.
【样例输出2】
6
【样例输入3】
3 2
3@
2#
1#
【样例输出3】
-1
HINT
题解
d f s dfs dfs玄学复杂度过去了……
注:这题用 b f s bfs bfs的时间复杂度更低,为 O ( n m 4 k ) O(nm4^k) O(nm4k)。我只是考场上用了 d f s dfs dfs加小小的优化卡过去了。这篇题解只是讲用 d f s dfs dfs怎么做一个小优化,不想看的可以跳过。
这个优化就是在处理如何维护贪吃蛇的位置(因为要防止贪吃蛇吃到自己)的问题上,如果每走一步都 O ( k ) O(k) O(k)更新一遍会比较慢(虽然在 k < = 9 k<=9 k<=9的情况下也慢不了多少),所以我们打算每次通过 O ( 1 ) O(1) O(1)来操作。
我们考虑为整个过程建一个时间,即贪吃蛇每走一格就时间 + 1 +1 +1秒,那么我们考虑定义一个数组 t i m e [ i ] [ j ] time[i][j] time[i][j],表示格子 [ i , j ] [i,j] [i,j]什么时候是空的。
我们不妨将贪吃蛇看成很多段,每段是一格长,那么贪吃蛇的头就是第一段,尾就是最后一段。
那么当游戏开始时,贪吃蛇的最后一段(尾部)的格子肯定至少在 1 1 1秒后才是空的,贪吃蛇的倒数第二段的格子肯定至少在 2 2 2秒后才是空的(第 1 1 1秒后,倒数第一段走到第 1 1 1秒前倒数第二段的格子,第 2 2 2秒后,倒数第一段离开)……贪吃蛇的第 i i i段的格子肯定至少在 k − i + 1 k-i+1 k−i+1秒后才是空的……贪吃蛇的第 1 1 1段的格子肯定至少在 k k k秒后才是空的。( k k k的解释见题面)
对于每当贪吃蛇走一步维护 t i m e time time的问题,我们发现只有贪吃蛇的头走到的格子的 t i m e time time才会有变化,那么我们考虑,假设当前时间为 t t t秒,贪吃蛇的头下一步要走到 [ x i , y i ] [xi,yi] [xi,yi]。
因为当前时间为 t t t秒,贪吃蛇走到下一个格子需要 1 1 1秒,然后等贪吃蛇完全经过需要 k k k秒,所以 t i m e [ x i ] [ y i ] = t + 1 + k time[xi][yi]=t+1+k time[xi][yi]=t+1+k秒,那么我们就能在 O ( 1 ) O(1) O(1)的时间内维护贪吃蛇位置的变化了。
最后完整代码加注释如下:
#include<bits/stdc++.h>
#define N 20
#define K 15
#define INF 0x7fffffff
using namespace std;
struct data
{
int x,y,s;
};
int n,k,m,edx,edy,ans=INF,tim[N][N],snax[N],snay[N];
//edx、edy为终点的坐标
//snax[]、snay[]为贪吃蛇的每一段的坐标
int fx[]={-1,0,1,0},fy[]={0,1,0,-1};
char ch[N][N];
bool vis[N][N];//是否走过
bool check(int x,int y)
{
return x<1||x>n||y<1||y>m;
}
void dfs(int x,int y,int s,int t)
{
if(x==edx&&y==edy)
{
ans=min(ans,s);
return;
}
if(s>=ans)return;
for(int i=0;i<4;i++)
{
int xi=x+fx[i],yi=y+fy[i];
if(vis[xi][yi]||check(xi,yi)||ch[xi][yi]=='#'||tim[xi][yi]-t>1)continue;
int tt=tim[xi][yi];
tim[xi][yi]=t+k+1;//time值更新
vis[xi][yi]=true;//记录贪吃蛇来过这个位置,防止死循环
dfs(xi,yi,s+1,t+1);
vis[xi][yi]=false;//记得改回来哦
tim[xi][yi]=tt;
}
}
void bfs()
{
queue<data>q;
q.push((data){snax[1],snay[1],0});
while(!q.empty()&&(q.front().x!=edx||q.front().y!=edy))
{
data now=q.front();
q.pop();
for(int i=0;i<4;i++)
{
int xi=now.x+fx[i],yi=now.y+fy[i];
if(vis[xi][yi]||check(xi,yi)||ch[xi][yi]=='#')continue;
vis[xi][yi]=true;
q.push((data){xi,yi,now.s+1});
}
}
if(q.empty())puts("-1");
else printf("%d\n",q.front().s);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%s",ch[i]+1);
for(int j=1;j<=m;j++)
{
if(ch[i][j]=='@')
{
edx=i,edy=j;
}
if('1'<=ch[i][j]&&ch[i][j]<='9')
{
snax[ch[i][j]-'0']=i,snay[ch[i][j]-'0']=j;
k=max(k,ch[i][j]-'0');
}
}
}
bool flag=true;
for(int i=0;i<4;i++)//特判终点的四周被堵住了
{
int xi=edx+fx[i];
int yi=edy+fy[i];
if(!check(xi,yi)&&ch[xi][yi]!='#')
{
flag=false;
break;
}
}
if(flag)
{
puts("-1");
return 0;
}
if(k==1)//特判k=1用bfs更快
{
bfs();
return 0;
}
for(int i=1;i<=k;i++)
tim[snax[i]][snay[i]]=k-i+1;//第0秒时的time
dfs(snax[1],snay[1],0,0);
if(ans!=INF)printf("%d\n",ans);
else puts("-1");
return 0;
}
总结
用时间复杂度高的算法时,要么想另一种做法,要么手造大数据测试并丧心病狂地卡常。