1. 程式人生 > 實用技巧 >L3-014 周遊世界 bfs

L3-014 周遊世界 bfs

周遊世界是件浪漫事,但規劃旅行路線就不一定了…… 全世界有成千上萬條航線、鐵路線、大巴線,令人眼花繚亂。所以旅行社會選擇部分運輸公司組成聯盟,每家公司提供一條線路,然後幫助客戶規劃由聯盟內企業支援的旅行路線。本題就要求你幫旅行社實現一個自動規劃路線的程式,使得對任何給定的起點和終點,可以找出最順暢的路線。所謂“最順暢”,首先是指中途經停站最少;如果經停站一樣多,則取需要換乘線路次數最少的路線。

輸入格式:
輸入在第一行給出一個正整數N(≤),即聯盟公司的數量。接下來有N行,第i行(,)描述了第i家公司所提供的線路。格式為:

M S[1] S[2] ⋯ S[M]

其中M(≤)是經停站的數量,S[i](,)是經停站的編號(由4位0-9的數字組成)。這裡假設每條線路都是簡單的一條可以雙向執行的鏈路,並且輸入保證是按照正確的經停順序給出的 —— 也就是說,任意一對相鄰的S[i]和S[i+1](,)之間都不存在其他經停站點。我們稱相鄰站點之間的線路為一個運營區間,每個運營區間只承包給一家公司。環線是有可能存在的,但不會不經停任何中間站點就從出發地回到出發地。當然,不同公司的線路是可能在某些站點有交叉的,這些站點就是客戶的換乘點,我們假設任意換乘點涉及的不同公司的線路都不超過5條。

在描述了聯盟線路之後,題目將給出一個正整數K(≤),隨後K行,每行給出一位客戶的需求,即始發地的編號和目的地的編號,中間以一空格分隔。

輸出格式:
處理每一位客戶的需求。如果沒有現成的線路可以使其到達目的地,就在一行中輸出“Sorry, no line is available.”;如果目的地可達,則首先在一行中輸出最順暢路線的經停站數量(始發地和目的地不包括在內),然後按下列格式給出旅行路線:

Go by the line of company #X1 from S1 to S2.
Go by the line of company #X2 from S2 to S3.
......
其中Xi是線路承包公司的編號,Si是經停站的編號。但必須只輸出始發地、換乘點和目的地,不能輸出中間的經停站。題目保證滿足要求的路線是唯一的。

輸入樣例:
4
7 1001 3212 1003 1204 1005 1306 7797
9 9988 2333 1204 2006 2005 2004 2003 2302 2001
13 3011 3812 3013 3001 1306 3003 2333 3066 3212 3008 2302 3010 3011
4 6666 8432 4011 1306
4
3011 3013
6666 2001
2004 3001
2222 6666
輸出樣例:
2
Go by the line of company #3 from 3011 to 3013.
10
Go by the line of company #4 from 6666 to 1306.
Go by the line of company #3 from 1306 to 2302.
Go by the line of company #2 from 2302 to 2001.
6
Go by the line of company #2 from 2004 to 1204.
Go by the line of company #1 from 1204 to 1306.
Go by the line of company #3 from 1306 to 3001.
Sorry, no line is available.


找了幾篇非dfs的部落格,都有點問題,只保證了最短路徑沒保證最少換乘,不過資料較弱也都給過了。dfs暴力過的不明白時間複雜度怎麼算的,我造了幾組資料給我tle了。
要以bfs來做的話,正常以點作為bfs節點來跑會出現多路徑問題,不能保證最短路是最少換乘,所以應該以邊作為bfs節點,即可同時保證最短路和最少換乘。網上有的給的是dijkstra,其實沒必要,直接bfs就好。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 10;

struct Graph {int v, i;};
struct Edge {int u, v, t;};
struct Node {int s, c, p;};
struct Path {int i, t;};
vector<Graph>g[N];
vector<Edge>e;
vector<Node>d;
vector<Path>path;
int bfs(int st, int ed) {
	for (int i = 0; i < e.size(); i++) d[i] = {(int)1e9, (int)1e9, -1};
	path.clear();
	int mn = 1e9, ret = 1e9;

	queue<int>q;
	for (auto p:g[st]) {
		d[p.i] = {1, 0, -1};
		q.push(p.i);
	}
	while (!q.empty()) {
		int u = q.front(); q.pop();
		if (d[u].s > ret) break;
		if (e[u].v == ed) {
			ret = d[u].s;
			if (d[u].c < mn) {
				mn = d[u].c;
				path.clear();
				path.push_back({ed, e[u].t});
				int r = u, l = d[r].p;
				while (l != -1) {
					if (e[l].t != e[r].t) {
						path.push_back({e[l].v, e[l].t});
					}
					r = l, l = d[r].p;
				}
				reverse(path.begin(), path.end());
			}
		}
		for (auto p:g[e[u].v]) {
			int v = p.i;
			if (d[v].s > d[u].s + 1) {
				d[v].s = d[u].s + 1;
				d[v].c = d[u].c + (e[u].t != e[v].t);
				d[v].p = u;
				q.push(v);
			} else if (d[v].s == d[u].s + 1) {
				if (d[v].c > d[u].c + (e[u].t != e[v].t)) {
					d[v].c = d[u].c + (e[u].t != e[v].t);
					d[v].p = u;
				}
			}
		}
	}
	return (ret == 1e9 ? -1 : ret);
}
signed main() {
	ios::sync_with_stdio(false);
	int n;
	cin >> n;
	for (int i = 1; i <= n; i++) {
		int k, u, v;
		cin >> k >> u;
		while (k-- > 1) {
			cin >> v;
			e.push_back({u, v, i});
			d.push_back({0, 0, 0});
			g[u].push_back({v, (int)e.size() - 1});
			e.push_back({v, u, i});
			d.push_back({0, 0, 0});
			g[v].push_back({u, (int)e.size() - 1});
			u = v;
		}
	}
	int k;
	cin >> k;
	while (k--) {
		int s, e;
		cin >> s >> e;
		int ans;
		if ((ans = bfs(s, e)) == -1) cout << "Sorry, no line is available." << '\n';
		else {
			cout << ans << '\n';
			for (int i = 0, p = s; i < path.size(); i++) {
				cout << "Go by the line of company #" << path[i].t << " from ";
				cout << setw(4) << setfill('0') << p;
				cout << " to ";
				cout << setw(4) << setfill('0') << path[i].i;
				cout << "." << '\n';
				p = path[i].i;
			}
		}
	}
	return 0;
}