1044.独轮车
时限:1000ms 内存限制:10000K 总时限:3000ms
描述
独轮车的轮子上有红、黄、蓝、白、绿(依顺时针序)5种颜色
在一个如下图所示的20*20的迷宫内每走一个格子,轮子上的颜色变化一次(原地转向则不变色)
独轮车只能向前推或在原地转向。每走一格或原地转向90度均消耗一个单位时间。
现给定一个起点(S)和一个终点(T),求独轮车以轮子上的指定颜色(未指定车头朝向)到达终点所需的最短时间。
输入
本题包含一个测例。
测例中分别用一个大写字母表示方向和轮子的颜色,其对应关系为:E-东、S-南、W-西、N-北;R-红、Y-黄、B-蓝、W-白、G-绿。
在测试数据的第一行有以空格分隔的两个整数和两个大写字母,分别表示起点的坐标S(x,y)、轮子的颜色和开始的方向,
第二行有以空格分隔的两个整数和一个大写字母,表示终点的坐标T(x,y)和到达终点时轮子的颜色(不指定结束时方向),
从第三行开始的20行每行内包含20个字符,表示迷宫的状态。其中'X'表示建筑物,'.'表示路.
输出
在单独的一行内输出一个整数,即满足题目要求的最短时间。
#include <iostream>
#include <queue>
using namespace std;
/******************全局变量***************************/
char maze[21][21]; //存放迷宫
struct node //记录独轮车的状态
{
int x; //第x行,从1开始
int y; //第y列,从1开始
int color; //车轮颜色,0==红,1==黄,2==蓝,3==白,4==旅
int dire; //车头朝向,0==西,1==南,2==东,3==北
};
node start,target; //起始状态,末状态
queue <node> state; //广搜所用队列
int used[21][21][5][4]; //此状态是否到达过
int time[21][21][5][4]; //到达此状态所用的时间
/******************全局变量***************************/
/******************函数声明***************************/
void input(); //输入数据所用函数
node inputState(int x, int y, char c, char d); //输入初始状态/末状态函数
bool bfs(); //广搜,成功返回true
bool canmoveto(node n1); //判断n1状态能否向前走一格
node moveto(node n1); //返回n1状态向前走一格后达到的状态(同时更新used数组和time数组)
bool canturnto(node n1, int i); //判断n1状态能否向i方向原地转向,i=0表示逆时针九十度,i=1顺时针九十度
node turnto(node n1, int i); //返回n1状态向i方向转向后到达的状态(同时更新used数组和time数组)
bool ftarget(node n1); //判断n1状态是不是目标状态
/******************函数声明***************************/
int main()
{
input(); //输入数据
if(bfs()) //广搜
{
//输出从初始状态到目标状态所用时间,因为目标状态只指定了坐标和颜色,
//与“车头朝向”无关,因此设车头朝向为西,即target.dire=0
cout<<time[target.x][target.y][target.color][0]<<endl;
}
else
{
cout<<"error"<<endl;
}
return 0;
}
/***********以下均为函数实现***************************/
void input() //输入数据
{
int x,y;
char c,d;
//输入初始状态
cin>>x>>y>>c>>d;
start=inputState(x,y,c,d);
//输入目标状态
cin>>x>>y>>c;
target=inputState(x,y,c,'W'); //末状态的dire不重要,因此设为西
//输入地图
for(int i=1; i<21; i++)
{
for(int j=1; j<21; j++)
{
cin>>maze[i][j];
}
}
}
node inputState(int x, int y, char c, char d) //输入一个状态
{
node n1;
n1.x=x;
n1.y=y;
switch(c) //输入颜色,将字符转化为数字
{
case 'R':n1.color=0;break;
case 'Y':n1.color=1;break;
case 'B':n1.color=2;break;
case 'W':n1.color=3;break;
case 'G':n1.color=4;break;
}
switch(d) //输入车头朝向,将字符转化为数字
{
case 'W':n1.dire=0;break;
case 'S':n1.dire=1;break;
case 'E':n1.dire=2;break;
case 'N':n1.dire=3;break;
}
return n1;
}
bool bfs() //核心算法,广搜
{
//将初始状态入队
state.push(start);
//更新used数组:标记此状态已到达
used[start.x][start.y][start.color][start.dire]=1;
//更新time数组:起点到起点所用时间为0
time[start.x][start.y][start.color][start.dire]=0;
//开始搜索
node top;
while(!state.empty())
{
top=state.front(); //取队首元素
state.pop();
//尝试向前走一格
if(canmoveto(top)) //如果top状态能够往前走一格
{
node next=moveto(top); //获得前进一格之后的状态next
state.push(next); //将next状态入队
if(ftarget(next)) //如果next状态就是target状态,退出搜索
{
return true;
}
}
//else{cout<<"不能走"<<top.x<<' '<<top.y<<' '<<top.dire<<endl;}
//尝试原地转向
for(int i=0; i<2; i++) //i=0表示逆时针转九十度,i=1表示顺时针转九十度
{
if(canturnto(top, i)) //如果top状态能够向i方向转九十度
{
node next=turnto(top, i); //获得转向之后的状态next
state.push(next); //将next状态入队
if(ftarget(next)) //如果next状态就是target状态,退出搜索
{
return true;
}
}
}
}
return false; //如果队列为空,仍未搜到目标状态,返回false
}
//判断身处状态n1,能否向“前”走一格,“前”就是车头朝向
//不可走条件:越界、前面是墙、状态重复
bool canmoveto(node n1)
{
switch(n1.dire) //根据车头朝向,判断应朝哪个方向走一格
{
case 0: //W 西(左)
{
if(n1.y-1<1 //越界
||maze[n1.x][n1.y-1]=='X' //或前方是墙
||used[n1.x][n1.y-1][(n1.color+1)%5][n1.dire]==1) //或状态重复
{
return false;
}
break;
}
case 1: //N 南(下)
{
if(n1.x+1>20 //越界
||maze[n1.x+1][n1.y]=='X' //或前方是墙
||used[n1.x+1][n1.y][(n1.color+1)%5][n1.dire]==1) //或状态重复
{
return false;
}
break;
}
case 2: //E 东(右)
{
if(n1.y+1>20 //越界
||maze[n1.x][n1.y+1]=='X' //或前方是墙
||used[n1.x][n1.y+1][(n1.color+1)%5][n1.dire]==1) //或状态重复
{
return false;
}
break;
}
case 3: //B 北(上)
{
if(n1.x-1<1 //越界
||maze[n1.x-1][n1.y]=='X' //或前方是墙
||used[n1.x-1][n1.y][(n1.color+1)%5][n1.dire]==1) //或状态重复
{
return false;
}
break;
}
}
return true;
}
//返回身处n1,向前走一格后达到的状态n2(并更新used数组和time数组)
node moveto(node n1)
{
node n2;
n2.dire=n1.dire; //向前走一格后,车头朝向依然不变
n2.color=(n1.color+1)%5; //颜色向前轮转一个,防止向上溢出
switch(n1.dire) //根据车头朝向,判断前进一格后的横纵坐标
{
case 0: //西(左)
{
n2.x=n1.x;
n2.y=n1.y-1; //面向西走一格
break;
}
case 1: //南(下)
{
n2.x=n1.x+1;
n2.y=n1.y;
break;
}
case 2: //东(右)
{
n2.x=n1.x;
n2.y=n1.y+1;
break;
}
case 3: //北(上)
{
n2.x=n1.x-1;
n2.y=n1.y;
break;
}
}
//更新used数组,标记n2状态已到达过
used[n2.x][n2.y][n2.color][n2.dire]=1;
//更新time数组,到达n2状态,比到达n1状态多用了一个时间
time[n2.x][n2.y][n2.color][n2.dire] = 1 + time[n1.x][n1.y][n1.color][n1.dire];
return n2;
}
//判断能否向i方向原地转向,0==逆时针九十度,1==顺时针九十度
//不可转条件:状态重复
bool canturnto(node n1, int i)
{
if(i==0) //逆时针转九十度
{
if(used[n1.x][n1.y][n1.color][(n1.dire+1)%4]==1)
{
return false;
}
}
else
{
if(used[n1.x][n1.y][n1.color][(n1.dire+4-1)%4]==1)
{
return false;
}
}
return true;
}
//返回身处n1,向i方向转九十度后到达的状态n2(并更新used数组和time数组)
node turnto(node n1, int i)
{
node n2;
n2.x=n1.x; //原地转向,位置不变
n2.y=n1.y;
n2.color=n1.color; //原地转向,颜色不变
if(i==0) //逆时针转九十度
{
n2.dire=(n1.dire+1)%4; //更新车头朝向,防止向上溢出
}
else //顺时针转九十度
{
n2.dire=(n1.dire+4-1)%4; //更新车头朝向,防止向下溢出
}
//更新used数组,标记n2状态已到达
used[n2.x][n2.y][n2.color][n2.dire]=1;
//更新time数组,从起点到达n2状态,比从起点到n1状态,多用了一个时间
time[n2.x][n2.y][n2.color][n2.dire] = 1 + time[n1.x][n1.y][n1.color][n1.dire];
return n2;
}
//判断n1是否到达目标状态
bool ftarget(node n1)
{
//目标状态的dire不重要,故不加入判断条件
if(n1.x==target.x&&n1.y==target.y&&n1.color==target.color) //若到达目标状态
{
//将从起点到达状态n1(dire=任意)的时间,赋值给我们指定的目标状态target(dire=0)
time[target.x][target.y][target.color][0]=time[n1.x][n1.y][n1.color][n1.dire];
return true;
}
else
{
return false;
}
}
【后记】
1.第一次感觉代码写的心力交瘁……累skr人……大概也不会有人看这么又臭又长的代码叭……