https://loj.ac/problem/10030
题目描述
给出一个虚拟键盘,键盘上有一光标,可以上下左右移动,移动时沿该方向一直移动到不同字符,求将给定字符串输出的最小步数(初始位置在左上角,移动为一步,点击键盘为一步,字符串结尾有换行符,用(‘*’)表示)。
思路
首先每一步移动时都可能会移动多步,而数据范围又比较小,所以我们可以先预处理出从((i,j))位置出发沿四个方向所移动的步数。接下来就开始(bfs)。不过这道题的特殊之处是我们需要存储两个量,表示到((i,j))这个位置经过(step)步最多能输出多少个给定的字符。这样我们就可以用一个数组来存到((i,j))最多完成几个字符的输出,就可以减少不必要的求解。而对于目前所在的位置就是要输出的字符特殊处理一下即可。
代码
#include <bits/stdc++.h>
using namespace std;
struct aa
{
int x,y,step,num;
aa(int x=0,int y=0,int step=0,int num=0):x(x),y(y),step(step),num(num){}
};
int dx[5]={1,-1,0,0},dy[5]={0,0,1,-1};
int v[60][60],a[60][60][5];
char goal[10100],key[60][60];
queue<aa> q;
int main()
{
int len,ans,r,c;
scanf("%d%d",&r,&c);
for(int i=1;i<=r;i++)
scanf(" %s",key[i]+1);
scanf(" %s",goal+1);
goal[strlen(goal+1)+1]='*';
len=strlen(goal+1);
for(int i=1;i<=r;i++)
for(int j=1;j<=c;j++)
for(int k=0;k<4;k++)
{
int nx=i,ny=j,t=0;
while(nx>0&&nx<=r&&ny>0&&ny<=c)
{
if(key[nx][ny]!=key[i][j])break ;
nx+=dx[k];ny+=dy[k];t++;
}
if(nx>0&&nx<=r&&ny>0&&ny<=c)
a[i][j][k]=t; //从(i,j)沿k方向要走多少步
}
v[1][1]=1; //到(i,j)最多输出多少字符
q.push(aa(1,1,0,1));
while(!q.empty())
{
aa nex,now=q.front();q.pop();
// cout<<now.x<<' '<<now.y<<' '<<goal[now.num]<<endl;
if(key[now.x][now.y]==goal[now.num]&&v[now.x][now.y]<now.num+1)
{
// cout<<now.x<<' '<<now.y<<' '<<goal[now.num]<<endl;
v[now.x][now.y]=now.num+1;
nex=aa(now.x,now.y,now.step+1,now.num+1);
if(nex.num>len){ans=nex.step;break ;}
q.push(nex);
}
else
{
for(int i=0;i<4;i++)
{
int bias=a[now.x][now.y][i];
if(bias)
{
nex=aa(now.x+bias*dx[i],now.y+bias*dy[i],now.step+1,now.num);
// cout<<nex.x<<' '<<nex.y<<' '<<goal[nex.num]<<endl;
if(nex.num<=v[nex.x][nex.y])continue ;
v[nex.x][nex.y]=nex.num;
q.push(nex);
}
}
}
}
printf("%d",ans);
return 0;
}