- 从节点1到节点n的路,可以分为“宽路+窄路+宽路+窄路…”或者“窄路+宽路+窄路+宽路…”。不管怎样,窄路和宽路交替出现。
- 将多段相连的短窄路放在一起考虑,在节点之间转移时,可以避免记录【到上一个节点时,最后走过的窄路的长度】。
- 计算的时候要小心溢出,还要考虑有重边的情况。
#include <iostream>
#include <queue>
#include <climits>
using namespace std;
#define MAX_N 501
int wide[MAX_N][MAX_N];
int narr[MAX_N][MAX_N];
int n;
int dist_wide[MAX_N];
int dist_narr[MAX_N];
int main() {
int m;
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j)
wide[i][j] = narr[i][j] = INT_MAX;
dist_wide[i] = dist_narr[i] = INT_MAX;
}
while (m--) {
int t, a, b, c;
cin >> t >> a >> b >> c;
if (t)
narr[a][b] = narr[b][a] = min(narr[a][b], c);
else
wide[a][b] = wide[b][a] = min(wide[a][b], c);
}
for (int k = 1; k <= n; ++k)
for (int i = 1; i <= n; ++i) if (narr[i][k] != INT_MAX)
for (int j = 1; j <= n; ++j) if (narr[k][j] != INT_MAX)
if (narr[i][j] > narr[i][k] + narr[k][j])
narr[i][j] = narr[i][k] + narr[k][j];
dist_narr[1] = dist_wide[1] = 0;
queue<int> que;
que.push(1);
vector<bool> seen(n + 1);
seen[1] = true;
while (!que.empty()) {
int u = que.front();
que.pop();
seen[u] = false;
for (int v = 1; v <= n; ++v) {
bool flag = false;
if (dist_narr[v] > dist_wide[u] + (long long)narr[u][v] * narr[u][v]) {
flag = true;
dist_narr[v] = dist_wide[u] + narr[u][v] * narr[u][v];
}
if (dist_wide[v] > (long long)min(dist_narr[u], dist_wide[u]) + wide[u][v]) {
flag = true;
dist_wide[v] = min(dist_narr[u], dist_wide[u]) + wide[u][v];
}
if (flag && !seen[v]) {
que.push(v);
seen[v] = true;
}
}
}
cout << min(dist_wide[n], dist_narr[n]) << endl;
return 0;
}