$$Keyboarding题解$$
【题目传送门】
【题意】 :
其实题意已经很清楚了,但是还是觉得在叙述一下。首先按键次数和选择次数是不一样的,我一开始就混了,区别提醒一下。还有就是不要忘记结尾处有一个换行符。
【思路分析】:
代码就最后附上吧,里面也有点注释,可以帮助理解
看到这个题,首先看到数据范围 \(n ,m \leq 50,\),挺显然得,要么\(DP\),要么是搜索,但是仔细看一下这道题,和深搜和宽搜的练习题好像呀,都是什么向上移动呀,向下移动之类的。我们确定是搜索。那我们用搜索去做就\(OK\)了。
我们可以思考一下其思路。force 废话。那么就考虑一下暴力怎么写,我们进行搜索的时候,往往就陷入这种沉思之中,先考虑一下搜什么,我的做法是去搜点,搜索每一个能到达的点。并且用\(opt\)来记录一下现在选择了几个数了,或者说,匹配了几个数了。
那么搜索的终止条件也就是 \(if(opt + 1 == len )\) ,那么这时候我们只需要在选择按下键位即可,总共也就是\(step+1\)(step为到达当前该点的按键次数) ;
如果没到, 那么怎么去搜索, 我们继续向下搜索,假设当前的节点为\(now\),
那么出现两种情况
1.这一个点的字符和下一个我们要打的一致,
2.这一个点的字符和下一个我们要打的不一致
考虑一致的时候:
首先我们发现这个时候特别简单,我们需要按下这个键位,这一点横纵坐标也不需要改,改的是\(opt和step\),也就是按键次数和选择的均加上一,压入队列即可。\(so\ \ easy\)吧
考虑不一致的时候:
这时我们发现 ,如果不一致,那么我们无从下手,因为我们不知道我们下一步往哪里走,不要急,下面再说,我们就假设我们下一个为\(next\)好吧,我们要看一下这一个点的选择和我们\(now\)这个点的选择次数,由于这个是从我们这个\(now\)蹦跶过去的,所以选择次数为\(now\)决定,\(so\),我们也就是可以和上面一样了,压入队列即可。
接下来就是我们的移动了
看一下这个图:无图无灵魂
1.向下走
我们看一下当它向下走的时候,这个时候,我们进行枚举
我们发现了$* \ \ * $这两个,那么我们向下的移动的位置也就是 \(now.x , now.y + 1\)了,那如果我们黄的也是$ * \(呢, 那么我们继续向下走,我们发现无论走到哪里,都是\) * \(,那么我们还走他干啥?,我们肯定不会选择呀,\)so$,我们选取在那一个方向上第一个不一样的作为该点的下一个移动位置就好了。
那么我们对于下界的枚举就是
for(int i = now.y ; i <= 总行数 ; 一行一行枚举)
{
if(发现不一样的),记录并且退出 。必须退出
}
其他的也一样,就不说了,就只说一下界限吧;
2.向上走
($ i = now.y \ \ ;\ \ i ≥ 1 ; i-- $)
3.想左走
($ i = now.x \ \ ;\ \ i ≥ 1 ; i-- $)
-
向右走
($ i = now.x \ \ ;\ \ i \leq 总列数; i++ $)
思路到此就结束了,附代码吧!
【code】:
/*
by : Zmonarch
思路: 1.多写几个函数,头脑清晰
2. 暴力搜索,就是麻烦一点(亿点,我就是菜,)
知识点 : force
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#define int long long
#define QwQ printf("运行过了\n") ;
#define inf 0x3f
using namespace std;
const int maxn = 600 ;
inline int read()
{
int x = 0 , f = 1 ; char ch = getchar() ;
while(!isdigit(ch)){ if(ch == '-') f = - 1 ; ch = getchar() ; }
while( isdigit(ch)){ x = x * 10 + ch - '0' ; ch = getchar() ; }
return x * f ;
}
int r , c ;
char str[maxn][maxn] , s[10000 + 5] ;
int G[maxn][maxn] , vis[maxn][maxn] ;
struct Point
{
int x , y , opt , step; //横坐标,纵坐标,选择次数和按键次数
} ;
struct Next //定义下一步的坐标,也就是看向上下左右行不行,行的下一个坐标
{
int x , y;
} ;
Next nxt[maxn][maxn][6] ;//下一步的坐标
queue<Point> q ;
int len = 1 ;
void move() //判断移动是否可行
{
//每一个点都进行判断
for(int i = 1 ; i <= r ; i++)
{
for(int j = 1 ; j <= c ; j++)
{
//判断向左
for(int jk = j - 1 ; jk >= 1 ; jk--)
{
if(str[i][j] != str[i][jk])
{
nxt[i][j][0] = (Next){ i , jk } ;
break ;
}
}
//判断向右
for(int jk = j + 1 ; jk <= c ; jk++)
{
if(str[i][j] != str[i][jk])
{
nxt[i][j][1] = (Next){i , jk} ;
break ;
}
}
//判断向上
for(int jk = i - 1 ; jk >= 1 ; jk--)
{
if(str[i][j] != str[jk][j])
{
nxt[i][j][2] = (Next){jk , j} ;
break ;
}
}
//判断向下
for(int jk = i + 1 ; jk <= r ; jk++)
{
if(str[i][j] != str[jk][j])
{
nxt[i][j][3] = (Next){jk , j} ;
break ;
}
}
}
}
}
void prepare()
{
r = read() , c = read() ;
len = 1 ;
for(int i = 1 ; i <= r ; i++)
{
scanf("%s" , str[i] + 1) ;
}
scanf("%s" , s + 1) ;
while(s[len]) len ++ ;
s[len] = '*' ;//要在结尾处添加一个换行符
move() ;
}
void search()
{
memset(vis , -1 , sizeof(vis)) ;
queue<Point> q ;
q.push((Point){1 , 1 , 0 , 0}) ;
while(!q.empty())
{
Point now = q.front() ;
q.pop();
int x = now.x , y = now.y ;
int opt = now.opt , step = now.step ;
if(str[x][y] == s[opt + 1])
{
q.push((Point){x , y , opt + 1 , step + 1}) ;
if(opt + 1 == len)
{
printf("%d\n" , step + 1 );
return ;
}
}
else
{
for(int i = 0 ; i <4 ; i++) //枚举一下四个可以走的下一个位置
{
int dx = nxt[x][y][i].x , dy = nxt[x][y][i].y;
if(dx == 0) continue ;
if(dy == 0) continue ;
if(vis[dx][dy] < opt)
{
vis[dx][dy] = opt ;
q.push((Point){dx , dy ,opt , step + 1}) ;//这个不选择
}
}
}
}
}
signed main()
{
prepare() ; //预处理出能够走位置
search() ;
return 0 ;
}