acwing.1137 选择最佳线路

https://www.acwing.com/problem/content/1139/

有一天,琪琪想乘坐公交车去拜访她的一位朋友。

由于琪琪非常容易晕车,所以她想尽快到达朋友家。

现在给定你一张城市交通路线图,上面包含城市的公交站台以及公交线路的具体分布。

已知城市中共包含 n 个车站(编号1~n)以及 m 条公交线路。

每条公交线路都是 单向的,从一个车站出发直接到达另一个车站,两个车站之间可能存在多条公交线路。

琪琪的朋友住在 s 号车站附近。

琪琪可以在任何车站选择换乘其它公共汽车。

请找出琪琪到达她的朋友家(附近的公交车站)需要花费的最少时间。

输入格式
输入包含多组测试数据。

每组测试数据第一行包含三个整数 n,m,s,分别表示车站数量,公交线路数量以及朋友家附近车站的编号。

接下来 m 行,每行包含三个整数 p,q,t,表示存在一条线路从车站 p 到达车站 q,用时为 t。

接下来一行,包含一个整数 w,表示琪琪家附近共有 w 个车站,她可以在这 w 个车站中选择一个车站作为始发站。

再一行,包含 w 个整数,表示琪琪家附近的 w 个车站的编号。

输出格式
每个测试数据输出一个整数作为结果,表示所需花费的最少时间。

如果无法达到朋友家的车站,则输出 -1。

每个结果占一行。

数据范围
n≤1000,m≤20000,
1≤s≤n,
0<w<n,
0<t≤1000
题意
给你多个点,让你计算多个点中任意一点到一定点的距离最小值
思路一.
因为本题是多对一,我们可以采用反向建边的方法把问题转化为一对多的形式,这样就可以只用一次最短路求min

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int N = 1010,M = 2 * 20010;
int head[N], ver[M], edge[M], ne[M], d[N];
bool v[N];
int n, m, s, w, tot;
priority_queue<pair <int, int> >q;
void add (int x, int y, int z) {
	ver[++tot] = y, edge[tot] = z, ne[tot] = head[x], head[x] = tot;
} 
void dijkstra (int s) {
	memset(d, 0x3f, sizeof d);
	memset(v, 0, sizeof v);
	d[s] = 0;
	q.push(make_pair(0, s));
	while (q.size()) {
		int x = q.top().second; q.pop();
		if (v[x]) continue;
		v[x] = 1;
		for (int i = head[x]; i; i = ne[i]) {
			int y = ver[i], z = edge[i];
			if (d[y] > d[x] + z) {
				d[y] = d[x] + z;
				q.push(make_pair(-d[y], y));
			}
		}
	}	
}
int main() {
	while (cin >> n >> m >> s) {
	int ans = INF;
	memset(head, -1, sizeof head);
	tot = 0;
	while (m--) {
		int p, q, t;
		cin >> p >> q >> t;
		add(q, p, t);
	}
	dijkstra(s);
	cin >> w;
	while (w--) {
		int a;
		cin >> a;
		ans = min(ans, d[a]); 
	}
	if (ans == INF) cout << -1 << endl;
	else cout << ans << endl;
	}
	return 0;
}

思路二.
我们可以建立一个虚拟源点,使其到每个起始点的距离变为0,最短路由这个虚拟源点开始,变为一对一的问题。
因为这样第一个进入优先队列的是这个虚拟源点,与它相邻的只有所有的起始点,且一定会松弛为0,
因此在效果上就等于把所有的起始点压入优先队列中。
// 虚拟源点 + dijkstra

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int N = 1010,M = 2 * 20010;
int head[N], ver[M], edge[M], ne[M], d[N];
bool v[N];
int n, m, s, w, tot;
priority_queue<pair <int, int> >q;
void add (int x, int y, int z) {
	ver[++tot] = y, edge[tot] = z, ne[tot] = head[x], head[x] = tot;
} 
int dijkstra () {
	memset(d, 0x3f, sizeof d);
	memset(v, 0, sizeof v);
	d[0] = 0;
	q.push(make_pair(0, 0));
	while (q.size()) {
		int x = q.top().second; q.pop();
		if (v[x]) continue;
		v[x] = 1;
		for (int i = head[x]; i; i = ne[i]) {
			int y = ver[i], z = edge[i];
			if (d[y] > d[x] + z) {
				d[y] = d[x] + z;
				q.push(make_pair(-d[y], y));
			}
		}
	}
	if (d[s] == INF) return -1;
	else return d[s];
}
int main () {
	while (cin >> n >> m >> s) {
	memset(head, -1, sizeof head);
	tot = 0;
	while (m--) {
		int p, q, t;
		cin >> p >> q >> t;
		add(p, q, t);
	}
	cin >> w;
	while (w--) {
		int a;
		cin >> a;
		add(0, a, 0);
	}
	cout << dijkstra() << endl;
	}
	return 0;
}

//把所有起始点压进有限队列 + dijkstra

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int N = 1010,M = 2 * 20010;
int head[N], ver[M], edge[M], ne[M], d[N];
bool v[N];
int n, m, s, w, tot;
priority_queue<pair <int, int> >q;
void add (int x, int y, int z) {
	ver[++tot] = y, edge[tot] = z, ne[tot] = head[x], head[x] = tot;
} 
int dijkstra () {
	while (q.size()) {
		int x = q.top().second; q.pop();
		if (v[x]) continue;
		v[x] = 1;
		for (int i = head[x]; i; i = ne[i]) {
			int y = ver[i], z = edge[i];
			if (d[y] > d[x] + z) {
				d[y] = d[x] + z;
				q.push(make_pair(-d[y], y));
			}
		}
	}
	if (d[s] == INF) return -1;
	else return d[s];
}
int main() {
	while (cin >> n >> m >> s) {
	memset(head, -1, sizeof head);
	memset(d, 0x3f, sizeof d);
	memset(v, 0, sizeof v);
	tot = 0;
	while (m--) {
		int p, q, t;
		cin >> p >> q >> t;
		add(p, q, t);
	}
	cin >> w;
	while (w--) {
		int a;
		cin >> a;
		q.push(make_pair(0, a));
		d[a] = 0;
	}
	cout << dijkstra() << endl;
	}
	return 0;
}

这里需要把d的初始化从dij函数中拉出来到main的循环里。
(感觉dijkstra码量和spfa差不多,没有负权我一般都用堆优化dij)
要记住初始化:
对于d[], v[], head[], tot 的初始化

posted @ 2022-04-20 10:17  misasteria  阅读(43)  评论(0编辑  收藏  举报