POJ 1847 - Tram(链式前向星存图 + SPFA + 读题)

萨格勒布的电车网络包括许多交叉路口和连接其中一些的铁路。在每个交叉路口,都有一个开关指向离开交叉路口的一条导轨。有轨电车进入交叉路口时,只能沿开关指向的方向离开。如果驾驶员想走其他路线,则必须手动更改开关。

当驾驶员确实从交叉路口A到交叉路口B行驶时,他/她试图选择一条路线,该路线将最大限度地减少他/她将不得不手动更改开关的次数。

编写一个程序,计算从路口A到路口B所需的最少开关变化次数。

Input

输入的第一行包含整数N,A和B,以单个空白字符分隔,2 <= N <= 100,1 <= A,B <= N,N是网络中的交点数,并且交叉点从1到N编号。

接下来的N行中的每行均包含由单个空白字符分隔的整数序列。第i行中的第一个数字Ki(0 <= Ki <= N-1)表示从第i个交叉点出来的轨道数。下一个Ki数字表示直接连接到第i个交叉点的交叉点。第i个交叉点的开关最初指向列出的第一个交叉点的方向。

Output

输出的第一行和唯一行应包含目标最小值。如果没有从A到B的路线,则该行应包含整数“ -1”。

Sample Input

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

Sample Output

0

题目大意:

在一个城市里有一个电车道路网,然后有一些岔路口,第一行输入三个数N A B 表示有N个岔路口,需要从A走到B,然后输入n行,对于第 i 行,先输入一个k表示有k个分叉,之后输入k个节点,j1…jk 表示从i -> j 是可行的,但是每个岔路口都有开关,默认的开关打到了每个岔路口的第一个路口,即3 1 2 3 表示有3个路口,可以通向1 2 3 ,但是开关默认指向1。 这句话很重要,影响到后面的建图,对于开关指向的区域可以直接走,对于没有指向的岔路口则需要手动操作,更改开关指向想要去的路口,最后要求输出A 到 B最小的操作数。

解题思路:

比较简单的一道最短路,重要的是读题,建图需要一些技巧,这里的权值就是操作数了。因为起初的开关指向第一个,所以从第二行开始,每行的第一个数权值应该是0,之后都是1才对,建有向边,之后跑spfa即可AC。

Code:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <iomanip>
#include <sstream>
#include <cmath>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <set>
#define lowbit(x) x & (-x)

using namespace std;

typedef long long ll;
typedef pair<int, int> pii;

const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const int N = 1e4 + 50;
const int M = 200 + 50;

int h[N], ne[N], e[N], w[N], idx;
int dis[N];
bool vis[N];
int n, a, b;

void add(int a, int b, int c)
{
	e[idx] = b;
	w[idx] = c;
	ne[idx] = h[a];
	h[a] = idx++;
}

void spfa(int s)
{
	memset(dis, 0x3f, sizeof dis);
	dis[s] = 0;

	queue<int > q;
	q.push(s);
	vis[s] = true;

	while (!q.empty())
	{
		int t = q.front();
		q.pop();
		vis[t] = false;

		for (int i = h[t]; ~i; i = ne[i])
		{

			int j = e[i];
			if (dis[t] + w[i] < dis[j])
			{
				dis[j] = dis[t] + w[i];
				if (!vis[j])
				{
					q.push(j);
					vis[j] = true;
				}
			}
		}
	}
}

int main()
{
	scanf("%d%d%d", &n, &a, &b);
	memset(h, -1, sizeof h);
	for (int i = 1; i <= n; i ++)
	{
		int k;
		scanf("%d", &k);
		for (int j = 1; j <= k; j ++)
		{
			int t;
			scanf("%d", &t);
			if (j == 1) add(i, t, 0);//第一个权值要建成00
			else add(i, t, 1);
		}
	}

	spfa(a);

	printf("%d", dis[b] == inf ? -1 : dis[b]);

	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值