题解
每个点有多个线路,不同线路之间切换需要额外代价,直接在队列的节点中记录当前线路编号增加额外代价无法得到最优解。
将原有节点拆分,使用map进行标号,如果当前节点有x个线路相连则将当前节点拆分为x个点,原边只在相同线路的点副本之间连接。
**每个节点,相邻站点编号的副本节点进行相连,代价为编号差。**不用每个副本都两两之间建立边,会导致极端复杂度M^2。
起始节点和终点的副本之间建立0代价边。表示从任意编号起点出发,到任意编号终点结束。最后dijkstra~。
AC代码
#include <stdio.h>
#include <bits/stdc++.h>
#define fst first
#define sed second
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e5 + 10;
int n, m;
map<pair<int, int>, int> mp;
int idx;
bool vis[N];
ll dis[N];
struct node
{
int v;
ll w;
bool operator < (const node &o) const
{
return w > o.w;
}
};
vector<node> e[N * 3];
vector<int> t[N]; //每个节点车站数量
int code(int v, int c) //给节点编号
{
int &res = mp[{v, c}];
if (!res)
res = ++idx;
return res;
}
ll dijkstra()
{
memset(vis, 0, sizeof(vis));
memset(dis, 0x3f, sizeof(dis));
int st = code(1, t[1][0]), ed = code(n, t[n][0]); //起点终点任意车站编号
priority_queue<node> pq;
pq.push({ st, 0 });
dis[st] = 0;
while (!pq.empty())
{
int u = pq.top().v;
if (u == ed)
return pq.top().w;
pq.pop();
vis[u] = 1;
for (auto it : e[u])
{
int v = it.v;
ll w = it.w;
if (!vis[v] && dis[u] + w < dis[v])
dis[v] = dis[u] + w, pq.push({ v, dis[v] });
}
}
return -1;
}
int main()
{
#ifdef LOCAL
freopen("C:/input.txt", "r", stdin);
#endif
while (cin >> n >> m)
{
mp.clear();
idx = 0;
for (int i = 0; i < m; ++i)
{
int u, v, c, w;
scanf("%d%d%d%d", &u, &v, &c, &w);
t[u].push_back(c); //记录车站
t[v].push_back(c);
u = code(u, c), v = code(v, c); //编码
e[u].push_back({ v, w });
e[v].push_back({ u, w });
}
for (int i = 1; i <= n; ++i)
{
vector<int> &v = t[i];
sort(v.begin(), v.end());
v.erase(unique(v.begin(), v.end()), v.end());
for (int j = 1; j < v.size(); ++j) //两个相邻编号建立额外边
{
int c1 = code(i, v[j - 1]), c2 = code(i, v[j]), w = v[j] - v[j - 1];
if (i == 1 || i == n)
w = 0; //起点终点的额外边权为0
e[c1].push_back({ c2, w });
e[c2].push_back({ c1, w });
}
}
ll res = dijkstra();
cout << res << endl;
for (int i = 1; i <= idx; ++i) e[i].clear();
for (int i = 1; i <= n; ++i) t[i].clear();
}
return 0;
}