【图论-最短路】电车

87 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第29天,点击查看活动详情

电车

题目描述

在一个神奇的小镇上有着一个特别的电车网络,它由一些路口和轨道组成,每个路口都连接着若干个轨道,每个轨道都通向一个路口(不排除有的观光轨道转一圈后返回路口的可能)。在每个路口,都有一个开关决定着出去的轨道,每个开关都有一个默认的状态,每辆电车行驶到路口之后,只能从开关所指向的轨道出去,如果电车司机想走另一个轨道,他就必须下车切换开关的状态。

为了行驶向目标地点,电车司机不得不经常下车来切换开关,于是,他们想请你写一个程序,计算一辆从路口 AA 到路口 BB 最少需要下车切换几次开关。

输入格式

第一行有 33 个整数 N,A,BN,A,B2N100,1A,BN2 \leq N \leq 100, 1 \leq A,B \leq N),分别表示路口的数量,和电车的起点,终点。

接下来有 NN 行,每行的开头有一个数字 KiK_i0KiN10 \leq K_i \leq N-1),表示这个路口与 KiK_i 条轨道相连,接下来有 KiK_i 个数字表示每条轨道所通向的路口,开关默认指向第一个数字表示的轨道。

输出格式

输出文件只有一个数字,表示从 AABB 所需的最少的切换开关次数,若无法从 AA 前往 BB,输出 1-1

样例 #1

样例输入 #1

3 2 1
2 2 3
2 3 1
2 1 2

样例输出 #1

0
//双端队列广搜,预计复杂度O(n) 
#include<bits/stdc++.h>
using namespace std;
int n,s,t;//s为起点,t为终点 
int e[105][105],k[105];//e存边,k表示每个节点出边条数 
int dis[105];//距离 
inline int read(){
    int x=0;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    while(ch<='9'&&ch>='0')x=(x<<3)+(x<<1)+ch-48,ch=getchar();
    return x;
}
deque<int> q;//双端队列是一种特别骚的STL,建议学一学,很简单。 
bool vis[105];
int main(){
	//初始化
	memset(vis,0,sizeof(vis));
	memset(dis,0x7f,sizeof(dis)); 
	//读入 
	n=read();s=read();t=read();
	for(int i=1;i<=n;i++){
		k[i]=read();
		for(int j=1;j<=k[i];j++){
			e[i][j]=read();
		}
	}
	q.push_back(s);dis[s]=0;
	//BFS 
	while(q.size()){
		int x=q.front();q.pop_front();
		if(vis[x])continue;//第一次取出就是该点最短路,
		//可避免点重复取出(类似DIJIKSTRA) 
		vis[x]=1;
		if(x==t){
			printf("%d\n",dis[t]);
			return 0;
		}
		//扫描出边 
		for(int i=1;i<=k[x];i++){
			int y=e[x][i];
			if(!k[y])continue;//剪枝:不到没有出边的点 
			if(i==1){
				dis[y]=min(dis[y],dis[x]);
				if(!vis[y])q.push_front(y);//因为dis[y]<=dis[x],所以在队列中最小
				//从头插入维护队列单调性 
			}
			else{
				dis[y]=min(dis[y],dis[x]+1);
				if(!vis[y])q.push_back(y);//因为dis[y]在dis[x]“下一层” , 
				//从尾插入维护队列单调性 
			}
		}
	}
	printf("-1");
	return 0;
}