给定一张带权有向图,已确定点 s s s 在 1 1 1 号点上,现图上还有另一点 t t t ,两点均可在该图上沿有向边移动。求 t t t 分别等于 [ 2 , n ] [2,n] [2,n] 时使得两点相遇的最小权值之和,无法相遇时输出 − 1 -1 −1。
智慧建图:第一层为原图,第二层为原图的反向图,两层中的对应点之间连一条权值为 0 0 0 的边,最终答案为由第一层的 1 1 1 号点到第二层的第 i i i 点间的最短路。
原理:由于两点均可移动,设两点相遇的中间点为 p p p ,则在原图中一定有 s − > p s->p s−>p 和 t − > p t->p t−>p 的路径存在,此时在第二层中建反向图,可以将 t − > p t->p t−>p 转化为 p − > t p->t p−>t ,相当于直接由点 s s s 为起点跑单源点最短路。而两层中对应点间权值为 0 0 0 的边,表示当前点为相遇点。
参考代码
#include
#define itn int
#define int long long
#define endl "\n"
#define PII pair<int, int>
using namespace std;
const int N = 2e5 + 10;
const itn inf = 0x3f3f3f3f;
const int mod = 998244353;
vector<PII> G[N];
struct node {
int u;
long long d;
bool operator<(const node& t) const { return d > t.d; }
};
void solve() {
int n, m;
cin >> n >> m;
while (m--) {
int u, v, w;
cin >> u >> v >> w;
G[u].emplace_back(v, w);
G[v + n].emplace_back(u + n, w);
}
for (int i = 1; i <= n; i++)
G[i].emplace_back(i + n, 0);
priority_queue<node> q;
int dis[N];
bool vis[N];
for (int i = 1; i <= n << 1; i++)
dis[i] = 1e18;
q.push({1, dis[1] = 0});
// dij
while (!q.empty()) {
int u = q.top().u;
q.pop();
if (vis[u])
continue;
vis[u] = 1;
for (auto now : G[u]) {
int v = now.first, w = now.second;
if (dis[v] > dis[u] + w)
q.push({v, dis[v] = dis[u] + w});
}
}
for (int i = 2; i <= n; i++) {
if (dis[i + n] == 1e18)
cout << "-1 ";
else
cout << dis[i + n] << " ";
}
cout << endl;
}
signed main() {
ios_base::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cout << fixed << setprecision(12);
// init();
int T = 1;
// cin >> T;
for (int i = 1; i <= T; i++) {
// cout << "Case #" << i << ": ";
solve();
}
return 0;
}