1452:Keyboarding
時間限制: 1000 ms 內存限制: 65536 KB
提交數: 433 通過數: 153
【題目描述】
出自 World Final 2015 F. Keyboarding
給定一個 r 行 c 列的在電視上的“虛擬鍵盤”,通過「上,下,左,右,選擇」共 5 個控制鍵,你可以移動電視屏幕上的光標來打印文本。一開始,光標在鍵盤的左上角,每次按方向鍵,光標總是跳到下一個在該方向上與當前位置不同的字符,若不存在則不移動。每次按選擇鍵,則將光標所在位置的字符打印出來。
現在求打印給定文本(要在結尾打印換行符)的最少按鍵次數。
【輸入】
第一行輸入 r,c。
接下來給出一個 r×c 的鍵盤,包括大寫字母,數字,橫線以及星號(星號代表 Enter 換行)。
最後一行是要打印的文本串 S,S 的長度不超過 10000。
【輸出】
輸出打印文本(包括結尾換行符)的最少按鍵次數。保證一定有解。
【輸入樣例】
2 19
ABCDEFGHIJKLMNOPQZY
X*****************Y
AZAZ
【輸出樣例】
19
【提示】
樣例輸入2:
5 20
12233445566778899000
QQWWEERRTTYYUUIIOOPP
-AASSDDFFGGHHJJKKLL*
–ZZXXCCVVBBNNMM–**
ACM-ICPC-WORLD-FINALS-2015
樣例輸出2:
160
樣例輸入3:
6 4
AXYB
BBBB
KLMB
OPQB
DEFB
GHI*
AB
樣例輸出 3
7
數據範圍:
對於 100% 的數據,1≤r,c≤50, S 的長度不超過 10000。
思路:樣例:假設我們要打印"AB"
1.多組數據
2.如果某方向上沒有不同字符光標不會動
我們每次預處理出每個點向四個方向下次到達的點。然後bfs即可
注意bfs每次只能擴展一層(也就是說距離dis每次最多隻能+1,否則無法保證最優性)!
處理字符用map
藍色框是一個一個字符去BFS,先找到一個,再找下一個,(每個箭頭算一步,因爲光標會且只會移動到該方向下一個不同的字符),紅色是正確走法
#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;
}