传送门
- 很经典的反向建边题目,AC代码使用SPFA做的,但是刚开始用的是朴素版的dijkstra,一直RE...
分析
- n个站点每个站点都要去一个人,求来回的总花销,那么可以确定的是第一个人不需要花钱,因为要从1号站台出发,所以第一个人不用动
- 路是单向的路,也就是有向图,从A到B和从B到A的花费很可能是不一样的
- 对于除第一个人之外的其他人,先到他的目标点,然后再从目标点回来,计算最短花费
- 因此,先正向跑一边最短路,再反向跑一遍最短路,最后计算总和就OK了
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <string>
using namespace std;
typedef long long ll;
const int N = 1000010;
bool vis[N];
struct Edge{
int v, w, next;
}edge[N], edge1[N];
int tot, head[N], head1[N];
ll dist[N], sum1[N], sum2[N], p, q;
void add(int u, int v, int w){
edge[tot].next = head[u];
edge[tot].v = v;
edge[tot].w = w;
head[u] = tot;
edge1[tot].next = head1[v];
edge1[tot].v = u;
edge1[tot].w = w;
head1[v] = tot ++;
}
void spfa1(){
queue<int> q;
memset(dist, 0x3f3f3f3f, sizeof dist);
memset(vis, 0, sizeof vis);
dist[1] = 0;
q.push(1);
vis[1] = 1;
while(q.size()){
int t = q.front();
q.pop();
vis[t] = false;
for(int i = head[t]; i != -1; i = edge[i].next){
int to = edge[i].v;
int w = edge[i].w;
if(dist[to] > dist[t] + w){
dist[to] = dist[t] + w;
vis[t] = true;
q.push(to);
}
}
}
}
void spfa2(){
queue<int> q;
memset(dist, 0x3f3f3f3f, sizeof dist);
memset(vis, 0, sizeof vis);
dist[1] = 0;
q.push(1);
vis[1] = 1;
while(q.size()){
int t = q.front();
q.pop();
vis[t] = false;
for(int i = head1[t]; i != -1; i = edge1[i].next){
int to = edge1[i].v;
int w = edge1[i].w;
if(dist[to] > dist[t] + w){
dist[to] = dist[t] + w;
vis[t] = true;
q.push(to);
}
}
}
}
int main(){
int t;
cin >> t;
while(t --){
memset(head, -1, sizeof head);
memset(head1, -1, sizeof head1);
memset(sum1, 0, sizeof sum1);
memset(sum2, 0, sizeof sum2);
scanf("%lld%lld", &p, &q);
while(q --){
ll a, b, p;
scanf("%lld%lld%lld", &a, &b, &p);
add(a, b, p);
}
spfa1();
for(int i = 1; i <= p; i ++)
sum1[i] = dist[i];
spfa2();
for(int i = 1; i <= p; i ++)
sum2[i] = dist[i];
ll res = 0;
for(int i = 2; i <= p; i ++)
res += sum1[i] + sum2[i];
printf("%lld
", res);
}
return 0;
}