版权声明:本人QQ 249712949 欢迎交流算法(请备注)。 https://blog.csdn.net/coord_/article/details/88903494
最短路。
这题瞎扯一大堆废话,其实就是一个有向图,先求从1
到其他点的最短路,再求从其他点到1
的最短路,上述2(n-1)
个最短路的值的和作为答案(题目保证是强连通图)。
从其他点到1
的最短路,我们不可能把其他每个点都作为起点运行一下,肯定会超时的。这就要用到反向建图。在正向图中某个点到1
的最短路就相当于在反向图中1
到这个点的最短路。(反向建图指的是把边的指向反转,和边权无关,想一想什么时候用到了边权反转?)所以只用来两次最短路算法就行了。
这道题我当时还用了链式前向星来存图,可以参考这个。
这种结构把最晚输入的边在边集中的序号作为这条边起点的head[]
值,然后以边集序号为索引,用next[]
连接一个又一个,就像在边集中从后往前跳跃,对应的边都是同一起点的。要注意next[]
数组的索引和边表的索引是一致的。
感觉这种结构比我之前写的邻接表要省空间。
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <utility>
#include <queue>
using namespace std;
const int INF = 1e9;
const int MAX = 1e6 + 2;
int N, P, Q;
bool inq[MAX];
int d[MAX];
int ans;
//vector<pair<int, int>> v[2][MAXP];
struct Edge
{
int n1, n2, w;
};
vector<Edge> ve;
int first[MAX], nxt[MAX];
void init()
{
memset(first, -1, sizeof first);
memset(nxt, -1, sizeof nxt);
ans = 0;
ve.clear();
}
void spfa(int id) // 这个id是指明spfa算法用边集中的n1还是n2作为当前点的后驱
{
fill(d + 1, d + P + 1, INF);
memset(inq, 0, sizeof inq);
queue<int> q;
d[1] = 0;
inq[1] = true;
q.push(1);
for (; !q.empty();)
{
int t = q.front();
q.pop();
inq[t] = false;
for (int i = first[t]; i != -1; i = nxt[i])
{
int u;
if (!id) u = ve[i].n2; //
else u = ve[i].n1;
int w = ve[i].w;
if (d[t] + w < d[u])
{
d[u] = d[t] + w;
if (!inq[u])
{
q.push(u);
inq[u] = true;
}
}
}
}
}
void reverse()
{
memset(first, -1, sizeof first);
memset(nxt, -1, sizeof nxt);
for (int i = 0; i < ve.size(); i++)
{
nxt[i] = first[ve[i].n2];
first[ve[i].n2] = i;
}
}
int main()
{
int a, b, c;
scanf("%d", &N);
for (; N--;)
{
scanf("%d%d", &P, &Q);
init();
for (; Q--;)
{
scanf("%d%d%d", &a, &b, &c);
ve.push_back(Edge{ a, b, c });
int index = ve.size() - 1;
nxt[index] = first[a]; // 链式前向星存图
first[a] = index;
}
spfa(0);
for (int i = 2; i <= P; i++) ans += d[i];
reverse();
spfa(1);
for (int i = 2; i <= P; i++) ans += d[i];
printf("%d\n", ans);
}
return 0;
}