UVA1714 Keyboarding
坑很多的一题
这里要感谢crk大佬提前帮我把所有的坑都踩了一遍...233
讲一下题目的意思:
给你一个神奇的 r*c 的键盘 (r,c<=50)
上面有大写的字母,数字,' - '号 和 ' * ' 号
有一个光标在键盘上
一开始在左上角,每次可以对光标进行一次操作:
向上,向下,向左,向右 和打印当前光标所在的字符
然后给你一个字符串(保证经过一些操作后可以打出来)
问你最少要几次操作才能打出给定的字符串
坑点:
1.在键盘上可能有很多的位置是同一种字符
2.如果从当前字符向某一个方向走,下一个字符和上一个字符一样
那么就自动跳过,直到找到下一个不一样的字符,而且只算走了一步
3.最后要在键盘上多选择一个 ' * ' 字符,表示回车
4.多组数据
数据这么小,肯定考虑搜索
预处理出每个点向4个方向到达的点
字符转成数字处理
用的是广搜,注意一次只走一步
不能走完了再选(算两步)
要分开来考虑
大概就这样吧,实现起来也不难
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<map> #include<queue> using namespace std; map <char,int> Map;//用map把字符转成数字 int n,m,ans; int mp[57][57],xx[4]={0,1,0,-1},yy[4]={1,0,-1,0};//mp存键盘状态 int nex[57][57][4][2];//nex存每个点向4个方向走,走到的点的横坐标和纵坐标 inline void premap()//预处理map { for(int i=1;i<=10;i++) Map[(char)('0'+i-1)]=i; for(int i=0;i<=25;i++) Map[(char)('A'+i)]=i+11; Map['-']=37; Map['*']=38; } inline void prenex()//预处理nex { for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) for(int k=0;k<4;k++) { int x=i+xx[k],y=j+yy[k]; while(mp[i][j]==mp[x][y]) x+=xx[k],y+=yy[k];//如果相同就继续走 nex[i][j][k][0]=x; nex[i][j][k][1]=y; } } struct node { int x,y,stp,dis; //x,y存横纵坐标,stp存当前已经选了几个字符,dis表示进行了几次操作 };//广搜的队列里每个点的内容 int lst[10007],vis[57][57],len; //lst存给定的字符串转成数字后的情况 //vis是记忆化数组,存走到点 i j 时选择的字符最多为多少 inline void bfs() { queue <node> q;//每次都重新开一个队列,如果重复用也可以,但是要注意清空 q.push( (node){1,1,0,0} );//把初始状态压入队列 while(!q.empty()) { node u=q.front(); q.pop(); if(mp[u.x][u.y]==lst[u.stp])//如果当前光标所在的字符是当前想要的字符 { if(u.stp==len)//如果是最后一个字符,就代表找到了最少步数 { ans=u.dis+1;//注意+1 return; } //否则 u.stp++; u.dis++;//选择此字符 vis[u.x][u.y]=u.stp;//更新vis q.push(u);//重新加入队列 } else//如果不是想要的字符 { int x,y; for(int k=0;k<4;k++)//尝试向4个方向移动 { x=nex[u.x][u.y][k][0]; y=nex[u.x][u.y][k][1]; if(x<1||x>n||y<1||y>m) continue;//判断越界 if(vis[x][y]>=u.stp) continue;//剪枝 vis[x][y]=u.stp; q.push( (node){x,y,u.stp,u.dis+1} );//新状态加入队列 } } } } char ch[10007]; int main() { premap(); while(scanf("%d",&n)!=EOF) { memset(nex,0,sizeof(nex)); memset(mp,0,sizeof(mp)); memset(lst,0,sizeof(lst)); memset(vis,-1,sizeof(vis));//注意初始化 ans=0; scanf("%d",&m); for(int i=1;i<=n;i++) { scanf("%s",ch); for(int j=0;j<m;j++) mp[i][j+1]=Map[ch[j]];//读入键盘并转成数字 } scanf("%s",ch); len=strlen(ch); for(int i=0;i<len;i++) lst[i]=Map[ch[i]];//读入字符串并转成数字 lst[len]=38;//最后要加一个'*'号 prenex(); bfs(); cout<<ans<<endl; } return 0; }