题意
有n个十字路口,NIO想从一段路到另一段路。NIO行驶到十字路口需选择一个方向,除了右转外都要等一次红绿灯(十字路口可以左转,右转,直行,掉头)。问NIO最少等几个红绿灯?
注意
路<a,b>和路<b,a>不同,路<a,b>指a路口->b路口,路<b,a>指b路口->a路口。如果想从路<a,b>变成路<b,a>,需要在b路口掉头一次。
做法
01最短路,dijkstra
难点在建图。
1.把每段带方向的路看成一个点,比如样例起点路<4,2>,可以看成点2,它的方向是2->4的方向而不是4->2(方便一会判断权值)。终点路<4,1>,可以看成点1,方向是1->4的方向。
2.记录每个路口的4个方向通向的路口,同时记录相连的路口之间的方向。
3.权值根据下个方向是否等于(当前点附带的方向+1)%4。等于表明是右转,权值为0;不等权值为1。
代码
#include<bits/stdc++.h>
#define PIP pair<int,pair<int,int>>
#define fi first
#define se second
using namespace std;
const int N=5e5+10,inf=0x3f3f3f3f;
int n,m,dist[N][5];
bool st[N][5];
int crs[N][5];//路口
unordered_map<int,int> dir[N];//方向
int dij(int st1, int st2, int ed1, int ed2)
{
memset(dist, 0x3f, sizeof dist);
dist[st2][dir[st2][st1]] = 0;
priority_queue<PIP, vector<PIP>, greater<PIP>> heap;
heap.push({0, {st2,dir[st2][st1]}});
while(heap.size())
{
PIP t=heap.top(); heap.pop();
int dis=t.fi, a=t.se.fi, b=t.se.se;
//距离,点,方向
if (st[a][b]) continue;
st[a][b]=1;
for(int i=0; i<4; ++i)
{
int tmp=(i== (b+1) % 4 ? 0 : 1);//根据方向要不要等红灯
int v=crs[a][i];
if(!v) continue;
if(dist[v][dir[v][a]]>dis+tmp)
{
dist[v][dir[v][a]]=dis+tmp;
heap.push({dist[v][dir[v][a]], {v, dir[v][a]}});
}
}
}
int res=dist[ed2][dir[ed2][ed1]];
if (res==inf) return -1;
return res;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
for(int j=0;j<4;j++)
{
scanf("%d",&crs[i][j]);
if(crs[i][j]) dir[i][crs[i][j]]=j;
}
}
int st1, st2, ed1, ed2;
scanf("%d%d%d%d",&st1,&st2,&ed1,&ed2);
printf("%d", dij(st1,st2,ed1,ed2));
return 0;
}