题意:
给定一个n*m的地图,给定了起点S和终点T。
某些格子上有障碍,有些格子上有楼梯。
有障碍的格子不能走,有楼梯的格子只有楼梯的方向跟行进方向相同时才能走。
楼梯每分钟变换一次方向(横向或竖向)。
每走一步花费1分钟,走楼梯的话可以直接走到楼梯对面,也是花费一分钟。
没有两个相邻的楼梯,人不能在楼梯上停留。
求需花费的最小时间。
算法:
按时间的奇偶性拆点,距离保存两个量,分别对应时间是奇数和时间是偶数时。
队列中的点具有时间上的单调性。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
#define st first
#define nd second
#define mp make_pair
using namespace std;
typedef pair<int,int> PII;//<pos,state> state表示时间的奇偶
int mm[25][25];
int d[25][25][2]; //d[x][y][state]
queue<PII> q;
int m,n,S,T;
int dx[]={1,-1,0,0,0};
int dy[]={0,0,1,-1,0};//注意考虑原地不动的情况
int get_id(int x,int y)
{
return (x-1)*m+y-1;
}
int main()
{
char str[25];
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(d,-1,sizeof(d));
memset(mm,-1,sizeof(mm));
for(int i=0;i<n;i++)
{
scanf("%s",str);
for(int j=0;j<m;j++)
{
if(str[j]=='|')
mm[i+1][j+1]=0;
else if(str[j]=='-')
mm[i+1][j+1]=1;
else if(str[j]=='.')
mm[i+1][j+1]=2;
else if(str[j]=='S')
{
mm[i+1][j+1]=2;
S = get_id(i+1,j+1);
}
else if(str[j]=='T')
{
mm[i+1][j+1]=2;
T = get_id(i+1,j+1);
}
}
}
while(!q.empty())
q.pop();
d[S/m+1][S%m+1][0]=0;
q.push(mp(S,0));
while(!q.empty())
{
int x = q.front().st;
if(x==T)
break;
int y = x%m+1;
x = x/m+1;
int state = q.front().nd;
q.pop();
//cout<<x<<' '<<y<<' '<<state<<endl;
for(int i=0;i<5;i++)
{
int nx = x+dx[i];
int ny = y+dy[i];
if(mm[nx][ny]==-1)
{
continue;
}
if(mm[nx][ny]<2 && ((mm[nx][ny]^state)==(i>>1))) //判断是否是楼梯在此时刻是否刚好变为和要走的方向相同
{
nx+=dx[i];
ny+=dy[i];
}
else if(mm[nx][ny]<2 && ((mm[nx][ny]^state)!=(i>>1)))
{
continue;
}
if(mm[nx][ny]==-1) //判断通过楼梯后的那块地是否可以走
{
continue;
}
if(d[nx][ny][state^1]==-1 || d[nx][ny][state^1]>d[x][y][state]+1)//最短路更新
{
d[nx][ny][state^1]=d[x][y][state]+1;
int tmp = get_id(nx,ny);
q.push(mp(tmp,state^1));
}
}
}
if(d[T/m+1][T%m+1][0]!=-1 && d[T/m+1][T%m+1][1]!=-1){
printf("%d\n",min(d[T/m+1][T%m+1][0],d[T/m+1][T%m+1][1])); //如果奇数时刻和偶数时刻都能走到
}else{
printf("%d\n",max(d[T/m+1][T%m+1][0],d[T/m+1][T%m+1][1])); //如果只有一个能走到必定走不到的还是为-1,所以较大值
}
}
return 0;
}