hiho 1643 最少換乘 O(n)複雜度
阿新 • • 發佈:2019-01-23
#1643 : 最少換乘
時間限制:10000ms 單點時限:1000ms 記憶體限制:256MB描述
小Ho居住的城市有N條公交車線路,其中第i條線路上有Ki個車站。
某些線路之間會有公共的車站,小Ho可以在這些車站從一條線路換乘到另一條線路。
現在給定N條公交車線路以及兩個車站S和E,你能幫助小Ho計算從S到E最少換乘幾次公交車嗎?
輸入
第一行包含三個整數N,S和E。
以下N行每行描述一條線路。第一個整數Ki代表該條線路包含的車站數。之後Ki個整數代表車站的編號。
注意車站編號不一定連續。
對於50%的資料,1 ≤ N ≤ 1000, 1 ≤ Ki ≤ 100
對於100%的資料,1 ≤ N ≤ 50000, 1 ≤ Ki
輸出
輸出最少換乘次數。如果S到E不可達,輸出-1。
樣例輸入3 123 345 4 321 375 123 456 4 222 333 123 444 2 222 345樣例輸出
1
思路:官方題解用的思路是最短路徑,我是直接用的廣搜解決。從起點所在的線路開始向臨近線路搜尋,先搜到
目標站點所線上路的搜尋路線,就是換乘最少的路線。挑了幾份最短路的程式碼提交比較時間,是比最短路演算法快
一點的,記憶體耗費也稍微少點。搜尋的時候,對搜過的線路,搜過的站點都做標記,保證只搜一遍,複雜度就
相當於遍歷一遍輸入的所有站點(包括重複的)。詳細實現原理看程式碼註釋
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int, int> pii; #define INF 0x3f3f3f3f #define FI first #define SE second int mod = 1000000007; const int N = 500010; vector<int> rec[N]; //第i個站點所在的線路 vector<int> load[N]; //第i條線路上的站點 bool yes[N]; //第i條線路是否是目標站點所線上路 bool vis[N]; //記錄第i個車站是否被遍歷過 bool line[N]; //記錄第i條線路是否被遍歷過 int main() { // freopen("in.txt", "r", stdin); int n; while (~scanf("%d", &n)) { int s, e, k, idx = 0; map<int, int> mp; //站點編號範圍太大了,實際站點個數很少離散處理一下 scanf("%d%d", &s, &e); mp.clear(); mp[s] = ++idx; mp[e] = ++idx; for (int i = 1; i <= n; i++) { load[i].clear(); rec[i].clear(); vis[i] = 0; line[i] = 0; } for (int i = 1; i <= n; i++) { yes[i] = 0; scanf("%d", &k); for (int j = 0; j < k; j++) { int x; scanf("%d", &x); load[i].push_back(x); //記錄第i條線所對應的站點 if (x == e) yes[i] = true; //第i條線路有目標站點,賦值true if (mp.find(x) == mp.end()) mp[x] = ++idx; rec[mp[x]].push_back(i); //x站點新增穿過它的線路 } } if (s == e) { puts("0"); continue; } queue<pair<int, int> > Q; Q.push(pii(mp[s], 0)); //佇列元素對應一個站點及到達這個站點的換乘次數 int ans = -1; vis[mp[s]] = true; while (!Q.empty()) { pii u = Q.front();Q.pop(); vector<int> &vec = rec[u.FI]; for (int i = 0; i < vec.size(); i++) { int v = vec[i]; if (yes[v]) //如果當前站點對應的某條線路含有目標站點,則找到了,跳出迴圈 { ans = u.SE; goto here; } if (line[v]) //該線路遍歷過則不去擴充套件 continue; line[v] = true; for (int j = 0; j < load[v].size(); j++)//擴充套件這條線路對應的其它站點, { //因為隊首站點之前的走過的線路 int vc = mp[load[v][j]]; //都被標記過了,沒標記的 if (!vis[vc]) //都是沒走過的,就相當於換乘一次了, { vis[vc] = true; Q.push(make_pair(vc, u.SE+1)); //換乘次數加1 } } } } here: printf("%d\n", ans); } return 0; }
複雜度分析:從程式碼看,由於做了標記每個點只入隊出隊一次,而每個點出隊後,不可避免要遍歷一遍這個點對應了
多少條線,每個點對應線路不同,不能直接乘法來計算;換個角度看,能遍歷得到的線路說明這條線上有這個站點,
總遍歷次數其實就是所給出的n條線裡面所有的站點數。即所有ki的和 <=500000。
而遍歷每條線路的程式碼是獨立開的,因為每條線最多訪問一次(可能有的線沒被訪問到就找到答案退出了)。
所以總次數也是所有ki的總和<=500000。演算法就是遍歷了所有輸入點2次,是O(N)的複雜度。