今日この質問をしました:最短経路を
検索しましたが、これは実際には古典的なグラフ理論の問題であることがわかりました:郵便配達員が手紙を送ります。2つの違いは、この質問が複数回あり、郵便配達員が手紙を送ることです。一度。そのため、q変数がコードのmain関数に追加されます。
この質問の特徴は、ある場所に到着するたびに、出発点に戻ってやり直さなければならないということです。最短経路問題として分類するのは簡単です。この質問の実現のアイデアは、ポジティブとネガティブな絵を構築することです。
アルゴリズムのアイデア:すべてのノードを1つずつ分割できます。行き来するプロセスは、開始点uからvまでの最短経路として理解できます。つまり、フォワードマッピングです。戻る過程で、すべてのエッジを反転してから、uからvへの最短経路を計算します(実際、私はこの考えを特に理解していません〜)。
インターネットでたくさんのコードを見つけましたが、他の人の書き込みに問題はなく、完全にAC対応です。しかし、私のような初心者にとって、それはあまり友好的ではありません。統一された機能があります:コメントはありません。そのため、理解するのに長い時間がかかり、最終的に各層の意味を理解しました。
グラフの作成は、主に隣接リストの方法です。構造側と組み合わせた1次元アレイヘッドを使用して表現します。前面と背面に2枚の写真があり、多くの素材が2枚を分けています。しかし、2次元のフォームを見つけるのは比較的簡単です。0は順方向を意味し、1は逆方向を意味します。最短パス方式はDJTeslaであり、優先キュー(すでにソート済み、最初にソート済み)を使用して実装されます。
ソースコードは次のとおりです。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mamx = 1e5+5;
// 之前有人把这个结构体定义为node,但其实里面信息都是有关于边的
struct side{
// v代表该边的指向
/*
* next代表相同开始边u连接的其他边,使用是邻接表的结构。但其实这个next很有歧义。
* 在Addedge中,next连接的是相同开始边u的不同v,但拼接的时候其实是一个反向更新
*/
int v, next;
ll w; // w代表该边的权重
}a[2][mamx]; // a代表的是边
ll num1, num2; // num1代表正向遍历当前的变数,num2代表反向遍历当前的边数
ll dis[2][mamx]; // dis代表各点据起始点的最短距离
ll head[2][mamx]; // head[][i]代表以i为起点,最新的边序号
bool visit[mamx]; // visit代表该节点是否已经被访问
int n, m;
void Addedge(int i, int u, int v, int w) {
int num;
if (i == 0)
num = num1;
else
num = num2;
// num通过先自增,这样num代表的是最新的边序号
a[i][++num].next = head[i][u]; // 采用倒序方法,当前边的next是上一条的编号
head[i][u] = num; // head[i][u]代表以u为起点的最新的边的编号
a[i][num].v = v;
a[i][num].w = w;
// 更新正向/反向当前的边数
if (i == 0)
num1 = num;
else
num2 = num;
}
/*
* 迪杰特斯拉的实现使用了优先队列,基本还是按照迪杰特斯拉的方法。
* 区别在于使用优先队列,免除了不断寻找最小值的的过程。
*/
void dij(int x) {
for (int i=1; i<=n; i++) {
dis[x][i] = 1e16; // 寻找最小值,传入的值应该尽可能大
visit[i] = 0;
}
priority_queue<pair<ll, int> > q; // ll类型代表权值,int类型是指向
dis[x][1] = 0;
q.push(make_pair(0,1));
while (!q.empty()) {
int u = q.top().second;
q.pop();
if (visit[u] == 1)
continue;
visit[u] = 1;
for (int i=head[x][u]; i; i=a[x][i].next) {
int v = a[x][i].v;
ll w = a[x][i].w;
if (dis[x][v] > dis[x][u] + w) {
dis[x][v] = dis[x][u] + w;
/*
此处传入的是负值,原因在于:我们希望是递增序列,这样从左到右遍历寻找
到的第一个visit[u]为false的就是当前最小的。但优先级队列默认是降序排列
此处加入负号,达到递增目的。
*/
q.push(make_pair(-dis[x][v], v));
}
}
}
}
int main(void) {
int q;
cin >> q;
while (q--) {
memset(head,0,sizeof(head));
cin >> n >> m;
num1 = 0;
num2 = 0;
for (int i=1; i<=m; i++) {
int u, v, w;
cin >> u >> v >> w;
Addedge(0,u,v,w);
Addedge(1,v,u,w);
}
ll ans = 0;
dij(0); // 0代表正向
dij(1); // 1代表反向
for (int i=2; i<=n; i++) {
// 自身不计算,从第二个点开始
ans += dis[0][i] + dis[1][i];
}
cout << ans << endl;
}
return 0;
}
参考資料:
https://blog.csdn.net/JiangHxin/article/details/104059688
https://blog.csdn.net/weixin_36888577/article/details/79937886