P6909 [ICPC2015 WF] Keyboarding 题解

本文介绍了如何解决ICPC2015WF竞赛中的Keyboarding问题,使用广度优先搜索策略规划字符跳动路径,通过剪枝优化算法减少不必要的搜索,最终实现按最少按键次数打印字符并以空格结束。

P6909 [ICPC2015 WF] Keyboarding 题解

我不理解,为什么这道题没有多组测试但是一大堆题解写了多测。

一道有坑的题。结尾要打印空格,即 *。而且最后搜到空格还要再按键。

解法

看到数据范围 n,m≤50n,m \le 50n,m50,于是考虑广度优先搜索。

因为光标总是跳到下一个在一方向上与当前位置不同的字符,所以我们考虑 O(n3)\mathcal{O}(n^3)O(n3) 预处理出每个位置上下左右能跳到哪里,方便下面的搜索。

搜索中我们记录搜到的位置、已经打印出的字符数量和按键次数。

对于每次搜索,如果现在搜到的位置可以打印出字符,那么我们就将其打印出来,再向 444 个方向搜索下面的字符。注意到我们用的是广度优先搜索,所以第一次打印完所有字符一定是最优解。

剪枝

  1. 考虑如果一个位置的一个方向上没有与这个位置不同的字符,那我们就不去搜索这个方向,可以通过预处理完成。

  2. 考虑到搜索时有可能出现从一个点开始向左搜索但是没有搜到答案又向右搜回这个点的情况,我们就标记搜到一个位置最多可以打印出的字符,记作 visi,jvis_{i,j}visi,j,如果搜到一个点现在打印出的字符比 visi,jvis_{i,j}visi,j 小,则剪枝。

代码

#include<bits/stdc++.h>
using namespace std;
namespace fast_IO
{
	/*
	快读快写,这里不贴了。忽略即可。
	*/
};
using namespace fast_IO;
int n,m,dx[100][100][5],dy[100][100][5],vis[100][100];
char a[100][100];
string st;
inline void init()
{
	for(int x=1;x<=n;x++)
		for(int y=1;y<=m;y++)
		{
			dx[x][y][2]=dx[x][y][4]=x,dy[x][y][1]=dy[x][y][3]=y;
			for(int i=x;i && a[x][y]==a[i][y];i--) dx[x][y][1]=i;
			for(int i=x;i<=n && a[x][y]==a[i][y];i++) dx[x][y][3]=i;
			for(int i=y;i<=m && a[x][y]==a[x][i];i++) dy[x][y][2]=i;
			for(int i=y;i && a[x][y]==a[x][i];i--) dy[x][y][4]=i;
			dx[x][y][1]--,dy[x][y][2]++,dx[x][y][3]++,dy[x][y][4]--;
		}
}
struct node
{
	int x,y,pos,cost;
};
node tmp;
deque<node> q;
inline bool inrange(const int &x,const int &y,const int &i)
{
	return (dx[x][y][i]>=1 && dx[x][y][i]<=n && dy[x][y][i]>=1 && dy[x][y][i]<=m);
}
int main()
{
	read(n),read(m),memset(vis,-1,sizeof(vis));
	for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) read(a[i][j]);
	read(st),st=st+"*",init(),q.clear(),q.push_back((node){1,1,0,0});
	while(!q.empty())
	{
		tmp=q.front(),q.pop_front();
		if(a[tmp.x][tmp.y]==st[tmp.pos])
		{
			if(tmp.pos==st.size()-1)
			{
				cout<<tmp.cost+1<<"\n";
				return 0;
			}
			vis[tmp.x][tmp.y]=tmp.pos+1,q.push_back((node){tmp.x,tmp.y,tmp.pos+1,tmp.cost+1});
		}else
		{
			for(int i=1;i<=4;i++)
				if(inrange(tmp.x,tmp.y,i) && vis[dx[tmp.x][tmp.y][i]][dy[tmp.x][tmp.y][i]]<tmp.pos)
					vis[dx[tmp.x][tmp.y][i]][dy[tmp.x][tmp.y][i]]=tmp.pos,
					q.push_back((node){dx[tmp.x][tmp.y][i],dy[tmp.x][tmp.y][i],tmp.pos,tmp.cost+1});
		}
	}
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值