思路:将无需改变的边看成边权是0, 将需要改变的边看成边权是1.
本题可以看成一个弱化版的dijkstra求最短路,则可以采用双端队列,每次从当前点扩展别的点时,若扩展到的点边权是0则放对头,如果是1则放队尾。由于队头每次是边权最短的点,则最终点一定是最短路(原理同dijkstra, 每次取出边权最短的边)
原图是字符二维数组,可以把节点想象出来,不用存储
、、、、、、、、、、、、、、、、、
#include<iostream>
#include<cstdio>
#include<cstring>
#include<deque>
#define f first
#define s second
using namespace std;
typedef pair<int, int> PII;
const int N=550, INF=0x3f3f3f3f;
int bfs();
int T;
int n, m;
char g[N][N];//原字符串图
int dist[N][N];//距离
bool st[N][N];//出队时,当前点是否遍历过。原因是二图
int dx[4]={-1, -1, 1, 1}, dy[4]={-1, 1, 1, -1};//节点坐标
int ix[4]={-1, -1, 0, 0}, iy[4]={-1, 0, 0, -1};//字符串坐标
char op[]={"\\/\\/"};//左上,右上,左下, 右下
//三个方向数组一一对应
signed main()
{
cin>>T;
while(T--)
{
scanf("%d%d", &n, &m);
for(int i=0;i<n;i++) scanf("%s", g[i]);
if((n+m)&1)//如果终点横纵坐标之和是奇数,则不可能达到
//这一性质,也决定了不会有,不在节点的交叉点.
{
puts("NO SOLUTION");
continue;
}
printf("%d\n", bfs());
}
}
int bfs()
{
memset(dist, 0x3f, sizeof dist);
memset(st, false, sizeof st);
deque<PII> q;
q.push_back({0, 0}), dist[0][0]=0;
while(q.size())
{
auto t=q.front();
q.pop_front();
if(st[t.f][t.s]) continue;
st[t.f][t.s]=true;
//这里判断,而不在下面判断,是否遍历过,是因为
//一个点的距离可能被更新多次,但这个点出队时一定是最短,推导为图2
for(int i=0;i<4;i++)
{
int a=t.f+dx[i], b=t.s+dy[i];
if(a<0||a>n||b<0||b>m) continue;
int ca=t.f+ix[i], cb=t.s+iy[i];
int d=dist[t.f][t.s]+(g[ca][cb]!=op[i]);//判断边权,即能走得线和实际的线是否相同,不同是1, 相同边权是0
if(d<dist[a][b])
{
dist[a][b]=d;
g[ca][cb]!=op[i]?q.push_back({a, b}):q.push_front({a, b});//相同放队头,否则放队尾
}
}
}
return dist[n][m];
}