D 坐地铁---分层图/加虚点/两种状态的dis
D 坐地铁
解法1:
数组用二维分别表示点(station)和线路(line)两种状态;跑最短路的过程中, 先将当前点经过的所有line入队, 在跑邻接点的时候, 看和当前点是否在同一线内, 在的话则更新。表示sp到i点的最短距离,最后到i的线是第j条线
#include<bits/stdc++.h> using namespace std; #define sc scanf #define MEM(x, b) memset(x, b, sizeof(x)) typedef long long ll; const int MOD = 1e9 + 7; const int MAXN = 2e3 + 100; const int INF = 0x3f3f3f3f; inline ll fpow(ll a, ll b){ ll r = 1, t = a; while (b){ if (b & 1)r = (r*t) % MOD; b >>= 1; t = (t*t) % MOD; }return r; } struct Edge { int v, k; }; struct node { int id, k, w; bool operator < (const node &oth) const { return w > oth.w; } }mid; vector <Edge> e[MAXN << 1]; vector <int> liEdge[MAXN << 1]; int dis[MAXN][MAXN], n, m, sp, tp; int in[MAXN], nex[MAXN];//dis[i][j]表示sp到i点的最短距离,最后到i的线是第j条线 bool vis[MAXN][MAXN]; void dijkstra() { priority_queue <node> q; MEM(dis, INF); MEM(vis, 0); for (auto it : liEdge[sp]) { // 起点线路全部入队 dis[sp][it] = in[it]; q.push({ sp, it, dis[sp][it] }); } while (!q.empty()) { mid = q.top(); q.pop(); int now = mid.id, line = mid.k; if (vis[now][line]) continue; vis[now][line] = true; for (auto it : liEdge[now]) { if (vis[now][it]) continue; if (dis[now][it] > dis[now][line] + in[it]) { //下一个线路 dis[now][it] = dis[now][line] + in[it]; q.push({ now, it, dis[now][it] }); } } for (int i = 0; i <e[now].size(); i++) { int vi = e[now][i].v, fi = e[now][i].k; // 同一条线才更新 if (fi != line) continue; if (dis[vi][fi] > dis[now][line] + nex[line]) { dis[vi][fi] = dis[now][line] + nex[line]; q.push({ vi, fi, dis[vi][fi] }); } } } } int main() { cin >> n >> m >> sp >> tp; for (int i = 1; i <= m; i++) { int num, tmp; sc("%d %d %d", &in[i], &nex[i], &num); //in表示进线,next表示下一站的费用 int last = 0; for (int j = 1; j <= num; j++) { sc("%d", &tmp); liEdge[tmp].push_back(i); // 该站台包含的线路 if (last) { e[last].push_back({ tmp, i }); // 依次连线 e[tmp].push_back({ last, i }); } last = tmp; } } if (sp == tp) { cout << 0 << endl; return 0; } dijkstra(); int ans = INF; for (int i = 1; i <= m; i++) ans=min(ans,dis[tp][i]); if (ans == INF) cout << -1 << endl; else cout << ans << endl; return 0; }
解法2:
这个解法代码更加简洁---dis只需要一维。手动添加一些虚点(每个点都有对应的虚点),在换线路时必须经过这些虚点,虚点到真实点的距离为"入场费",真实点到虚点距离为0,这样直接跑最短路即可
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int, int> P; const int maxn = 1e3 + 5; int n, m, s, t; int head[500 * maxn], cnt; int to[maxn * 1000], nxt[maxn * 1000], val[maxn * 1000]; void addEdge(int a, int b, int c) { to[++ cnt] = b, val[cnt] = c; nxt[cnt] = head[a], head[a] = cnt; } bool vis[maxn * 500]; int dis[maxn * 500]; void dijkstra(){ priority_queue<P> que; que.push(P(0, s)); memset(dis, 0x3f, sizeof dis); dis[s] = 0; while(!que.empty()) { P p = que.top(); que.pop(); int u = p.second; if(u == t) { cout << -p.first << endl; return ; } if(vis[u]) continue; vis[u] = 1; for(int i = head[u]; i; i = nxt[i]) { int v = to[i]; if(dis[v] > dis[u] + val[i]) { dis[v] = dis[u] + val[i]; que.push(P(-dis[v], v)); } } } cout << -1 << endl; } int main(){ cin >> n >> m >> s >> t; for(int i = 1, a, b, c; i <= m; i ++) { cin >> a >> b >> c; for(int j = 1, u, v; j <= c; j ++) { cin >> v; if(j > 1){ addEdge((i - 1) * n + u, (i - 1) * n + v, b); addEdge((i - 1) * n + v, (i - 1) * n + u, b); } addEdge((i - 1) * n + v, m * n + v, 0); addEdge( m * n + v, (i - 1) * n + v, a);//每个点都有一个虚点,入站费用 u = v; } } t = n * m + t;//转换成虚点 s = n * m + s; dijkstra(); return 0; }