World Final 2015 F. Keyboarding
Posted wangjieyu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了World Final 2015 F. Keyboarding相关的知识,希望对你有一定的参考价值。
Keyboarding
这是一道BFS(但用到了不少的SPFA的思想)的题,不过出题者十分毒瘤,有众多坑点:
1、按下方向键时跳到下一个不同的字符,也就意味着光标可以“传送”。
2、最后要打印一个回车,也就是“*”号。
3、选择也是一次操作。
4、可能有多组数据。
5、因为同样的字母会重复出现,所以如果BFS一个接一个的找时,第一个找到的未必是全局最优的,如图:
[样例2][1]
[1]: https://cdn.luogu.org/upload/pic/40343.png
若是一个一个字符去BFS,那顺序是(1)(2)(3)(4)(5)(6)(7),因为只有该方向有不同字符,光标才会移动,所以是不能从(2)到(11)的。而正确的路线是(1)(8)(9)(10)(6)(7)。所以从当前位置到下一个要打印的字符的最短路线不一定对打印后面的字符有利。
思路:
1、先预处理每个点可以到的地方(最多4个),存入f数组。
2、list数组保存每一个BFS到的点的x、y坐标,所用的步数(dep)及从出发点到这个点的路径中已打印的字符数(find)。
3、dep数组表示地图上每一个点作为一条路径的终点可以打印多少字符。
4、(重点)如果从出发点到当前的点再到另一个点比出发点到那个点的原路径所打印的字符更多,那么更新那个点的find和dep值,也就是说以这个点为终点,有另一条路径,并且路径上打印的字符更多(类似SPFA)。反则不更新,因为已经被遍历过的点所用步数肯定比当前的点的步数少。
5、如果当前点是下一个要打印的点,就find++,表示去寻找下一个字符。
6、找到最后一个字符后输出步数并return 0;
Code
(因为我太弱了不会用队列,只好有循环数组)
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int dx[4]={-1,0,1,0};//四个方向
int dy[4]={0,-1,0,1};//四个方向
char s[110000],map[2100][2100];
int r,c,xx,yy,len,head,tail,dep[2100][2100];
struct node
{
int x,y,dep,find;//x,y是坐标,dep表示当前所用步数,find表示以当前点为终点的路径中已打印的字符数
}list[1600000];
struct nodee
{
int x[5],y[5],len;//x,y数组表示可以去到的位置,len表示可前往的点的数量
nodee()
{
len=0;
}
}f[2100][2100];
void chuli()//预处理每个点能去到的地方,记录在f数组里
{
for(int i=1;i<=r;i++)
for(int j=1;j<=c;j++)
for(int k=0;k<=3;k++)
{
xx=i;
yy=j;
while(map[i][j]==map[xx+dx[k]][yy+dy[k]]&&
xx+dx[k]&&yy+dy[k])//一直循环直到下一个点是合法的或者到达边界
{
xx+=dx[k];
yy+=dy[k];
}
if(map[i][j]!=map[xx+dx[k]][yy+dy[k]]&&
xx+dx[k]&&yy+dy[k])//如果下一个是点是合法的,就加入f数组
{
f[i][j].len++;
f[i][j].x[f[i][j].len]=xx+dx[k];
f[i][j].y[f[i][j].len]=yy+dy[k];
}
}
return ;
}
int main()
{
while(scanf("%d%d",&r,&c)!=EOF)//多组数据
{
memset(f,0,sizeof(f));
memset(dep,-1,sizeof(dep));
memset(list,0,sizeof(list));
memset(map,‘ ‘,sizeof(map));//初始化
for(int i=1;i<=r;i++)
scanf("%s",map[i]+1);
scanf("%s",s+1);
len=strlen(s+1);
s[len+1]=‘*‘;
len++;//最后要打印一个回车,也就是“*”号
chuli();//预处理
xx=yy=1;
head=1;
tail=2;
list[1].x=xx;
list[1].y=yy;
list[1].dep=0;
list[1].find=0;
while(head!=tail)
{
if(map[list[head].x][list[head].y]==s[list[head].find+1])如果当前的点是下一个要打印的字符,就重新加入队列同时把已打字符数加1
{
list[tail]=list[head];
list[tail].dep++;
list[tail].find++;
if(list[tail].find==len)//如果所有数都打印了,就输出并结束。
{
printf("%d
",list[head].dep+1);
break;
}
tail++;
}
for(int k=1;k<=f[list[head].x][list[head].y].len;k++)//逐个遍历当前点可以去到的其他点
{
xx=f[list[head].x][list[head].y].x[k];
yy=f[list[head].x][list[head].y].y[k];
if(dep[xx][yy]<list[head].find)//这个dep数组与结构体中的dep不同,dep数组表示地图中每一个点作为终点,路径上最多能打印的字符数,如果这个点作为终点有可打印更多的字符,就更新它并加入队列去更新更多的点
{
list[tail].x=xx;
list[tail].y=yy;
list[tail].dep=list[head].dep+1;
list[tail].find=list[head].find;
dep[xx][yy]=list[head].find;
tail++;
if(tail>1500000)
tail=1;
}
}
head++;
}
}
return 0;
}
不要试图抄袭、复制或是删掉注释后提交代码,就算我不把你打死,你也AC不了的。(因为我修改了一点小东西,嘿嘿嘿)
以上是关于World Final 2015 F. Keyboarding的主要内容,如果未能解决你的问题,请参考以下文章
Problem: The World Final II(NEUOJ1175)排序+动态规划
World Cup(The 2016 ACM-ICPC Asia China-Final Contest dfs搜索)
Gym 101194L / UVALive 7908 - World Cup - [三进制状压暴力枚举][2016 EC-Final Problem L]