1. 程式人生 > 其它 >acwing.1137 選擇最佳線路

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 的初始化