洛谷 P1126 机器人搬重物

题目描述

机器人移动学会(RMI)现在正尝试用机器人搬运物品。机器人的形状是一个直径 1.61.6 米的球。在试验阶段,机器人被用于在一个储藏室中搬运货物。储藏室是一个 �×�N×M 的网格,有些格子为不可移动的障碍。机器人的中心总是在格点上,当然,机器人必须在最短的时间内把物品搬运到指定的地方。机器人接受的指令有:

  • 向前移动 11 步(Creep);
  • 向前移动 22 步(Walk);
  • 向前移动 33 步(Run);
  • 向左转(Left);
  • 向右转(Right)。

每个指令所需要的时间为 11 秒。请你计算一下机器人完成任务所需的最少时间。

输入格式

第一行为两个正整数 �,� (1≤�,�≤50)N,M (1≤N,M≤50),下面 �N 行是储藏室的构造,00 表示无障碍,11 表示有障碍,数字之间用一个空格隔开。接着一行有 44 个整数和 11 个大写字母,分别为起始点和目标点左上角网格的行与列,起始时的面对方向(东 �E,南 �S,西 �W,北 �N),数与数,数与字母之间均用一个空格隔开。终点的面向方向是任意的。

输出格式

一个整数,表示机器人完成任务所需的最少时间。如果无法到达,输出 −1−1。

输入输出样例

输入 #1复制

9 10
0 0 0 0 0 0 1 0 0 0
0 0 0 0 0 0 0 0 1 0
0 0 0 1 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0
0 0 0 0 0 0 1 0 0 0
0 0 0 0 0 1 0 0 0 0
0 0 0 1 1 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 1 0
7 2 2 7 S

输出 #1复制

12

思路如下:

那么这道题经过信息整理可以发现,我们需要两个数组:一个存方格上障碍物的位置(sd[i][j]数组表示),一个存机器人可以走的格点的位置(a[i][j])数组表示,则根据样例我们可以整理成这张图: 

样例图示

 其中黑色为数组sd[i][j]的下标,绿色为a[i][j]的下标。根据题目可以发现,机器人本身也有宽度,所以边界和障碍物的四周,不能走,那么机器人可以到达的地方就是图中蓝色框内的绿色格点。所以边界条件判断时,n和m各要减1。

然后我们还需要注意的地方是题中害人不浅的方向处理。于是为了方便存储,我给每个方向都编了一个号:↑为1,↓为2,←为3,→为4。然后转向的时候就更加麻烦了。于是我为了好判断情况,emm......用了好几个数组

int fx[5]={0,-1,1,0,0};//fx[i]表方向i(编号)的x的进退情况  
int fy[5]={0,0,0,-1,1};//fy[i]表方向i(编号)的y的进退情况 
int ft[5]={0,1,4,2,3};//ft[i]表示顺时针排列各个方向的编号(上1 右4 下2 左3) 
int fft[5]={0,1,3,4,2};//fft[i]表示数字i在ft[]数组中的下标 
int abc[5]={0,1,2,1,0};//abc[5]表示转到[顺时针转i次到达的那个方向]的最短次数 

其中ft数组和abc数组比较难理解

先讲abc数组 

abc数组解释

 如图,不难看出当对于不同的i,也就是顺时针转动i次时,对应的最小旋转次数就是abc[i]

而ft数组也比较好理解 abc图中蓝圈内四个方向各有一个黑色数字代表方向编号,那么我们顺时针遍历一下就是1 4 2 3.

我么还要注意的地方是起点和终点可能重合,需要特判;起点可能就有障碍物,也要特判。

最后要注意的是,对于每一步,你走一个格点,两个格点或三个格点所耗时间都是1,不要搞乱;而每转90°,就要耗一定时间,所以千万要小心。

于是,开始写BFS

用队列存储每一个格点的信息,然后起点入队,每次从队首取出一个元素,根据这个元素旋转,直行,得到一个新的坐标,然后判断由这种方式到达的这个点是否是耗时最短的一种方式(类似于Dijkstra中的dis数组),此处用f[i][j]数组表示,然后队列为空后,输出f[终点的x][终点的y]即可。

 

代码如下: 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<queue>
#include<cmath>
#include<iomanip>
using namespace std;
int sd[55][55];
int a[55][55];//a为读入的方格地图 
int n,m;
int x11,y11;//起点 
int x2,y2;//终点 
int f[55][55];//f为格点地图 

int fx[5]={0,-1,1,0,0};//fx[i]表方向i(编号)的x情况  
int fy[5]={0,0,0,-1,1};//fy[i]表方向i(编号)的y情况 
int ft[5]={0,1,4,2,3};//ft[i]表示顺时针排列各个方向的编号(上1 右4 下2 左3) 
int fft[5]={0,1,3,4,2};//fft[i]表示数字i在ft[]数组中的下标 

int abc[5]={0,1,2,1,0};//abc[5]表示转到[顺时针转i次到达的那个方向]的最短次数 
struct node
{
	int x,y;//当前点的坐标 
	int t;//1=>N 2=>S 3=>W 4=>E 方向编号 
	int time;//从起点到当前点的最短时间 
};
queue<node> q;//队列q 
string ch;//读入起点的方向 
int cto;//起点的方向 

void fxto()
{
	switch(ch[0])
	{
		case 'N': cto=1;break;
		case 'S': cto=2;break;
		case 'W': cto=3;break;
		case 'E': cto=4;break;
	}
	return;
}
void change()
{
	for(int i=1;i<=n;++i)
	{
		for(int j=1;j<=m;++j)
		{
			if(sd[i][j]==1)//如果当前格为障碍物,则它的四个顶点都不能走 
			{
				a[i-1][j]=1;
				a[i][j-1]=1;
				a[i-1][j-1]=1;
				a[i][j]=1;
			}
		}
	}
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;++i)
	{
		for(int j=1;j<=m;++j)
		{
			scanf("%d",&sd[i][j]);
		}
	}
	cin>>x11>>y11>>x2>>y2;
	cin>>ch;
	fxto();//判断ch代表的方向 
	change();//把方格地图转化为机器人可以走的格点地图 
	node first;//起点 
	first.x=x11;
	first.y=y11;
	first.t=cto;
	first.time=0;
	q.push(first);//起点入队 
	node u,d;
	while(!q.empty())
	{
		u=q.front();
		q.pop();
		for(int i=1;i<=4;++i)
		{
			int zhuan=abc[i];//[顺时针转i下的那个方向]的最短旋转次数 
			
			//求出旋转完了以后方向的编号fangx(为了方便讨论,全部当做顺时针旋转) 
			int fangx=fft[u.t]+i;//此时fangx为下标 
			if(fangx==5) fangx=1;
			if(fangx==6) fangx=2;
			if(fangx==7) fangx=3;
			if(fangx==8) fangx=4;
			fangx=ft[fangx];//此时fangx为方向编号 
			//此时fangx存的是由当前点顺时针转了i次后到达的方向的编号
			 
			for(int j=1;j<=3;++j)//走1~3步 
			{
				int lsx=u.x+fx[fangx]*j;//计算按当前旋转方向走j步的坐标 
				int lsy=u.y+fy[fangx]*j;
				if(lsx>=n || lsx<=0 || lsy>=m || lsy<=0 || (lsx==x11&&lsy==y11) || a[lsx][lsy]==1)
				{
					//判断边界和障碍物 (特判:是否为起点)
					break;
				}
				if((u.time+zhuan+1<f[u.x+fx[fangx]*j][u.y+fy[fangx]*j] || f[u.x+fx[fangx]*j][u.y+fy[fangx]*j]==0) && a[u.x+fx[fangx]*j][u.y+fy[fangx]*j]==0)
				{//如果当前点可以刷新距离,就入队 
					d.x=u.x+fx[fangx]*j;
					d.y=u.y+fy[fangx]*j; 
					d.t=fangx;
					d.time=u.time+zhuan+1;
					f[u.x+fx[fangx]*j][u.y+fy[fangx]*j]=d.time;
					q.push(d);
				}
			}
		}
	}
	if(f[x2][y2]==0 && (x2!=x11 || y2!=y11))//如果为0,代表不能走到 
	{
		cout<<"-1"<<endl;
	}
	else//否则输出终点的距离 
	cout<<f[x2][y2]<<endl;
	return 0;
}

拜拜!(*´▽`)ノシ 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值