原题链接:
城市街道交通费系统 - 洛谷https://www.luogu.com.cn/problem/P1300
题目描述
城市街道交费系统最近创立了。一辆汽车左转一次需付费 11 ,右转一次需付费 55 。只有当前进、左转、右转都无路可走的时候,调头才是允许的,调头每次付费 1010 。
给出一张城市地图,要求你求出从起始点到达终止点的花费最少的路径。幸运的是,所有的道路都是正北、正南、正西或正东方向的。
输入格式
输入格式如下:
- 第一行有两个整数,地图高度 hh 和宽度 ww。
- 其后 hh 行,每行 ww 个字符,将是以下字符中的一个:
.
表示障碍区。#
表示道路。E
表示起始点且汽车面朝东。W
表示起始点且汽车面朝西。N
表示起始点且汽车面朝北。S
表示起始点且汽车面朝南。F
表示终点。
输出格式
仅包含一个整数,即为最便宜路径的费用。
输入输出样例
输入 #1复制
8 11 ........... ....#####.. ....#...#.. ....#...#.. .#E######.. ....#...... .##F#...... ...........
输出 #1复制
8
输入 #2复制
17 21 ..................... .#######............. .#.....#.......#..... .###...#.......#..... ...#...#.......#..... .###...#.......#..... .#.....#.......#..... .############F#####.. .......#..........#.. .......#..........#.. ...#...#...#####..#.. ...#...#...#.#.#..#.. ..#S########.#.#..#.. ...#.......#.###..#.. ...#.......#......#.. ...........########.. .....................
输出 #2复制
7
说明/提示
样例一解释:
直走,然后左转 33 次,最后右转到终止点 F
。如果先直走然后右转 22 次,花费将是 1010 。
样例二解释:
最便宜的路径花费 7 :立刻左转,直走,在第一个岔路口左转,随后右转。
对于 100% 的数据:4≤h,w≤30。
数据保证地图中只有一个起点,一个终点,他们之间存在着可通达的路径。同时保证地图最外层一圈都是障碍。
解题思路:
一题复杂些的dfs题,但是需要有以下注意的地方
1.路段可能有交叉如果用传统的vis[x][y]方式打标记会导致不同的路走到交叉点时因为已经被标记过就误以为已经走过导致少搜路
2.尽管在同一个点,不同的方向也是不同的搜法,所以必须要用三维的标记数组第三维标记方向
3.与传统的搜索不同的是,你第一次搜索到map[x][y][direction]时并不一定是最优解,因为这题搜索花费取决于转向,而非路程。且从不同路段是能够搜索到同一个x,y,dirction的这时候就需要比较花费,而如果以第一次搜索到的结果做为最优解显然是不正确的。所以这里vis数组存储是搜索到该点的这个方向时的最优解,返回条件为第二次搜索到该点时的当前花费大于vis里存储的当前点最优解
4.地图有40x40如果不进行剪枝的话,40!的时间显然是我们所不能接受的。所以在这里进行一个最常见的剪枝:当搜索的花费已经大于当前搜索到的最优解时就return
AC代码:
#include<bits/stdc++.h>
using namespace std;
char ma[40][40];//地图数组
int n,m;
int bx,by,bdir,ex,ey;//分别表示,起点坐标,起点方向,终点坐标
char lft[128];//左转数组
char rit[128];//右转数组
char back[128];//回头数组
int dx[128],dy[128];//方向数组
int vis[40][40][128];//标记
int res = 0x3f3f3f3f;//答案
void init()//预处理每个方向左转,右转,回头后是哪个方向
{
lft['E'] = 'N';rit['E'] = 'S';back['E'] = 'W';
lft['W'] = 'S';rit['W'] = 'N';back['W'] = 'E';
lft['N'] = 'W';rit['N'] = 'E';back['N'] = 'S';
lft['S'] = 'E';rit['S'] = 'W';back['S'] = 'N';
dx['E'] = 0;dy['E'] = 1;dx['W'] = 0;dy['W'] = -1;dx['N'] = -1;dy['N'] = 0;dx['S'] = 1;dy['S'] = 0;//预处理每个方向的前进坐标
}
void dfs(int nowx,int nowy,char nowdir,int ans)
{
if(nowx==ex&&nowy==ey)//搜索到终点就更新答案
{
res = min(ans,res);
return;
}
if(vis[nowx][nowy][nowdir]<=ans||ma[nowx][nowy]=='.'||ans>=res) return;//剪枝
bool flag1 = true,flag2 = true,flag3 = true;
//前进
vis[nowx][nowy][nowdir] = ans;
int xx = nowx+dx[nowdir],yy = nowy+dy[nowdir];
if(ma[xx][yy] == '.') flag1 = false;
dfs(xx,yy,nowdir,ans);
//左转
xx = nowx+dx[lft[nowdir]],yy = nowy+dy[lft[nowdir]];
if(ma[xx][yy] == '.') flag2 = false;
dfs(xx,yy,lft[nowdir],ans+1);
//右转
xx = nowx+dx[rit[nowdir]],yy = nowy+dy[rit[nowdir]];
if(ma[xx][yy] == '.') flag3 = false;
dfs(xx,yy,rit[nowdir],ans+5);
//回头
if(!flag1&&!flag2&&!flag3)
{
xx = nowx+dx[back[nowdir]],yy = nowy+dy[back[nowdir]];
dfs(xx,yy,back[nowdir],ans+10);
}
}
int main()
{
cin>>n>>m;
init();
memset(ma,'.',sizeof(ma));
for(int i = 1;i<=n;++i)
for(int j = 1;j<=m;++j)
{
cin>>ma[i][j];
if(ma[i][j]=='E'||ma[i][j]=='N'||ma[i][j]=='S'||ma[i][j]=='W')
{
bx = i;
by = j;
bdir = ma[i][j];
}
if(ma[i][j]=='F')
ex = i,ey = j;
}
memset(vis,0x3f,sizeof(vis));//vis数组初始化为极大值
dfs(bx,by,bdir,0);
cout<<res<<endl;
return 0;
}